v/vlib/v/parser/fn.v

787 lines
21 KiB
V
Raw Normal View History

// Copyright (c) 2019-2021 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.token
import v.util
2020-01-02 08:30:15 +01:00
pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExpr {
2020-04-10 14:53:06 +02:00
first_pos := p.tok.position()
mut fn_name := if language == .c {
2020-06-18 20:21:08 +02:00
'C.$p.check_name()'
} else if language == .js {
2020-06-18 20:21:08 +02:00
'JS.$p.check_js_name()'
} else if mod.len > 0 {
2020-06-18 20:21:08 +02:00
'${mod}.$p.check_name()'
} else {
p.check_name()
}
if language != .v {
p.check_for_impure_v(language, first_pos)
}
2020-05-23 08:51:15 +02:00
mut or_kind := ast.OrKind.absent
2020-05-04 16:46:36 +02:00
if fn_name == 'json.decode' {
p.expecting_type = true // Makes name_expr() parse the type `User` in `json.decode(User, txt)`
2020-05-04 16:46:36 +02:00
p.expr_mod = ''
2020-05-23 08:51:15 +02:00
or_kind = .block
2020-05-04 16:46:36 +02:00
}
mut generic_types := []table.Type{}
mut generic_list_pos := p.tok.position()
if p.tok.kind == .lt {
// `foo<int>(10)`
2020-05-27 15:56:21 +02:00
p.expr_mod = ''
generic_types = p.parse_generic_type_list()
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
2020-05-29 04:36:08 +02:00
// In case of `foo<T>()`
// T is unwrapped and registered in the checker.
full_generic_fn_name := if fn_name.contains('.') { fn_name } else { p.prepend_mod(fn_name) }
has_generic_generic := generic_types.filter(it.has_flag(.generic)).len > 0
if !has_generic_generic {
// will be added in checker
p.table.register_fn_gen_type(full_generic_fn_name, generic_types)
}
}
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)
2020-06-06 12:28:03 +02:00
// ! in mutable methods
if p.tok.kind == .not {
p.next()
}
2021-01-19 14:49:40 +01:00
mut pos := first_pos.extend(last_pos)
mut or_stmts := []ast.Stmt{} // TODO remove unnecessary allocations by just using .absent
mut or_pos := p.tok.position()
2020-02-29 15:03:32 +01:00
if p.tok.kind == .key_orelse {
2020-05-23 08:51:15 +02:00
// `foo() or {}``
2020-05-21 22:35:43 +02:00
was_inside_or_expr := p.inside_or_expr
p.inside_or_expr = true
2020-02-29 15:03:32 +01:00
p.next()
p.open_scope()
p.scope.register(ast.Var{
name: 'err'
typ: table.string_type
pos: p.tok.position()
is_used: true
})
p.scope.register(ast.Var{
name: 'errcode'
typ: table.int_type
pos: p.tok.position()
is_used: true
})
2020-05-23 08:51:15 +02:00
or_kind = .block
or_stmts = p.parse_block_no_scope(false)
or_pos = or_pos.extend(p.prev_tok.position())
p.close_scope()
2020-05-21 22:35:43 +02:00
p.inside_or_expr = was_inside_or_expr
2020-02-29 15:03:32 +01:00
}
2020-05-12 00:09:59 +02:00
if p.tok.kind == .question {
// `foo()?`
p.next()
2020-05-23 08:51:15 +02:00
or_kind = .propagate
2020-05-12 00:09:59 +02:00
}
if fn_name in p.imported_symbols {
fn_name = p.imported_symbols[fn_name]
}
comments := p.eat_line_end_comments()
2021-01-19 14:49:40 +01:00
pos.update_last_line(p.prev_tok.line_nr)
return ast.CallExpr{
2020-01-02 08:30:15 +01:00
name: fn_name
args: args
mod: p.mod
2020-04-10 14:53:06 +02:00
pos: pos
language: language
generic_types: generic_types
generic_list_pos: generic_list_pos
2020-02-29 15:03:32 +01:00
or_block: ast.OrExpr{
stmts: or_stmts
2020-05-23 08:51:15 +02:00
kind: or_kind
pos: or_pos
}
scope: p.scope
comments: comments
2020-02-04 17:44:39 +01:00
}
2020-01-02 08:30:15 +01:00
}
pub fn (mut p Parser) call_args() []ast.CallArg {
mut args := []ast.CallArg{}
start_pos := p.tok.position()
for p.tok.kind != .rpar {
if p.tok.kind == .eof {
p.error_with_pos('unexpected eof reached, while parsing call argument', start_pos)
return []
}
is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic
is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
if is_mut {
p.next()
2020-02-04 12:50:58 +01:00
}
mut comments := p.eat_comments()
arg_start_pos := p.tok.position()
mut array_decompose := false
if p.tok.kind == .ellipsis {
p.next()
array_decompose = true
}
mut e := p.expr(0)
if array_decompose {
e = ast.ArrayDecompose{
expr: e
pos: p.tok.position()
}
}
if mut e is ast.StructInit {
e.pre_comments << comments
comments = []ast.Comment{}
}
pos := arg_start_pos.extend(p.prev_tok.position())
comments << p.eat_comments()
args << ast.CallArg{
is_mut: is_mut
share: table.sharetype_from_flags(is_shared, is_atomic)
expr: e
comments: comments
pos: pos
}
if p.tok.kind != .rpar {
p.check(.comma)
}
}
return args
}
fn (mut p Parser) fn_decl() ast.FnDecl {
p.top_level_statement_start()
2020-04-19 00:07:57 +02:00
start_pos := p.tok.position()
is_manualfree := p.is_manualfree || p.attrs.contains('manualfree')
is_deprecated := p.attrs.contains('deprecated')
2020-08-24 09:04:50 +02:00
is_direct_arr := p.attrs.contains('direct_array_access')
mut is_unsafe := p.attrs.contains('unsafe')
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-22 13:11:58 +02:00
p.open_scope()
2020-04-15 23:16:49 +02:00
// C. || JS.
mut language := table.Language.v
if p.tok.kind == .name && p.tok.lit == 'C' {
2020-08-09 11:22:11 +02:00
is_unsafe = !p.attrs.contains('trusted')
language = table.Language.c
} else if p.tok.kind == .name && p.tok.lit == 'JS' {
language = table.Language.js
}
if language != .v {
2020-02-04 09:54:15 +01:00
p.next()
p.check(.dot)
p.check_for_impure_v(language, p.tok.position())
2020-02-04 09:54:15 +01:00
}
// Receiver?
mut rec_name := ''
mut is_method := false
mut receiver_pos := token.Position{}
mut rec_type_pos := token.Position{}
mut rec_type := table.void_type
mut rec_mut := false
2020-09-27 03:32:56 +02:00
mut params := []table.Param{}
if p.tok.kind == .lpar {
lpar_pos := p.tok.position()
2020-04-21 05:07:49 +02:00
p.next() // (
2020-01-07 13:10:05 +01:00
is_method = true
is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic
rec_mut = p.tok.kind == .key_mut || is_shared || is_atomic
2020-04-15 01:45:27 +02:00
if rec_mut {
p.next() // `mut`
2020-04-15 01:45:27 +02:00
}
rec_start_pos := p.tok.position()
rec_name = p.check_name()
2020-04-15 01:45:27 +02:00
if !rec_mut {
rec_mut = p.tok.kind == .key_mut
2020-05-17 13:51:18 +02:00
if rec_mut {
p.warn_with_pos('use `(mut f Foo)` instead of `(f mut Foo)`', lpar_pos.extend(p.peek_tok2.position()))
2020-05-17 13:51:18 +02:00
}
2020-04-15 01:45:27 +02:00
}
receiver_pos = rec_start_pos.extend(p.tok.position())
is_amp := p.tok.kind == .amp
if p.tok.kind == .name && p.tok.lit == 'JS' {
language = table.Language.js
}
// 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_pos = p.tok.position()
2020-04-16 15:40:21 +02:00
rec_type = p.parse_type_with_mut(rec_mut)
if rec_type.idx() == 0 {
// error is set in parse_type
return ast.FnDecl{
scope: 0
}
}
rec_type_pos = rec_type_pos.extend(p.prev_tok.position())
if is_amp && rec_mut {
p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`',
lpar_pos.extend(p.tok.position()))
return ast.FnDecl{
scope: 0
}
}
if is_shared {
rec_type = rec_type.set_flag(.shared_f)
}
if is_atomic {
rec_type = rec_type.set_flag(.atomic_f)
}
2020-09-27 03:32:56 +02:00
params << table.Param{
pos: rec_start_pos
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)
}
mut name := ''
if p.tok.kind == .name {
pos := p.tok.position()
2020-03-11 21:11:27 +01:00
// TODO high order fn
2020-06-18 20:21:08 +02:00
name = if language == .js { p.check_js_name() } else { p.check_name() }
if language == .v && !p.pref.translated && util.contains_capital(name) && !p.builtin_mod {
p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead',
pos)
return ast.FnDecl{
scope: 0
}
}
type_sym := p.table.get_type_symbol(rec_type)
// interfaces are handled in the checker, methods can not be defined on them this way
if is_method && (type_sym.has_method(name) && type_sym.kind != .interface_) {
p.error_with_pos('duplicate method `$name`', pos)
return ast.FnDecl{
scope: 0
}
}
// cannot redefine buildin function
if !is_method && !p.builtin_mod && name in builtin_functions {
p.error_with_pos('cannot redefine builtin function `$name`', pos)
return ast.FnDecl{
scope: 0
}
}
}
if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt, .eq, .ne, .le, .ge]
&& p.peek_tok.kind == .lpar {
2020-04-21 05:07:49 +02:00
name = p.tok.kind.str() // op_to_fn_name()
if rec_type == table.void_type {
p.error_with_pos('cannot use operator overloading with normal functions',
p.tok.position())
}
p.next()
}
2020-02-03 07:44:52 +01:00
// <T>
generic_params := p.parse_generic_params()
2020-01-02 08:30:15 +01:00
// Args
args2, are_args_type_only, is_variadic := p.fn_args()
2020-09-27 03:32:56 +02:00
params << args2
if !are_args_type_only {
for param in params {
if p.scope.known_var(param.name) {
p.error_with_pos('redefinition of parameter `$param.name`', param.pos)
return ast.FnDecl{
scope: 0
}
}
p.scope.register(ast.Var{
name: param.name
typ: param.typ
is_mut: param.is_mut
pos: param.pos
is_used: true
is_arg: true
})
}
2020-02-11 13:03:10 +01:00
}
mut end_pos := p.prev_tok.position()
2020-01-02 08:30:15 +01:00
// Return type
mut return_type := table.void_type
if p.tok.kind.is_start_of_type()
|| (p.tok.kind == .key_fn && p.tok.line_nr == p.prev_tok.line_nr) {
return_type = p.parse_type()
2020-01-02 08:30:15 +01:00
}
mut type_sym_method_idx := 0
no_body := p.tok.kind != .lcbr
2020-02-27 17:21:13 +01:00
// Register
2020-01-08 10:19:12 +01:00
if is_method {
mut type_sym := p.table.get_type_symbol(rec_type)
// Do not allow to modify / add methods to types from other modules
// arrays/maps dont belong to a module only their element types do
// we could also check if kind is .array, .array_fixed, .map instead of mod.len
mut is_non_local := type_sym.mod.len > 0 && type_sym.mod != p.mod && type_sym.language == .v
// check maps & arrays, must be defined in same module as the elem type
if !is_non_local && type_sym.kind in [.array, .map] {
elem_type_sym := p.table.get_type_symbol(p.table.value_type(rec_type))
is_non_local = elem_type_sym.mod.len > 0 && elem_type_sym.mod != p.mod
&& elem_type_sym.language == .v
}
if is_non_local {
p.error_with_pos('cannot define new methods on non-local type $type_sym.name',
rec_type_pos)
return ast.FnDecl{
scope: 0
}
}
2020-07-01 18:57:14 +02:00
// p.warn('reg method $type_sym.name . $name ()')
type_sym_method_idx = type_sym.register_method(table.Fn{
2020-01-08 10:19:12 +01:00
name: name
2020-09-27 03:32:56 +02:00
params: params
return_type: return_type
2020-03-24 12:39:11 +01:00
is_variadic: is_variadic
generic_names: generic_params.map(it.name)
is_pub: is_pub
2020-06-23 16:25:24 +02:00
is_deprecated: is_deprecated
is_unsafe: is_unsafe
no_body: no_body
mod: p.mod
attrs: p.attrs
2020-01-08 10:19:12 +01:00
})
} else {
if language == .c {
name = 'C.$name'
} else if language == .js {
2020-04-15 23:16:49 +02:00
name = 'JS.$name'
} else {
name = p.prepend_mod(name)
}
if _ := p.table.find_fn(name) {
p.fn_redefinition_error(name)
}
2020-07-01 18:57:14 +02:00
// p.warn('reg functn $name ()')
2020-01-07 13:10:05 +01:00
p.table.register_fn(table.Fn{
name: name
2020-09-27 03:32:56 +02:00
params: params
return_type: return_type
is_variadic: is_variadic
generic_names: generic_params.map(it.name)
is_pub: is_pub
2020-06-23 16:25:24 +02:00
is_deprecated: is_deprecated
is_unsafe: is_unsafe
no_body: no_body
mod: p.mod
attrs: p.attrs
language: language
2020-01-07 13:10:05 +01:00
})
}
end_pos = p.prev_tok.position()
2020-04-08 19:08:54 +02:00
// Body
2020-06-06 21:36:24 +02:00
p.cur_fn_name = name
mut stmts := []ast.Stmt{}
body_start_pos := p.peek_tok.position()
2020-02-04 09:54:15 +01:00
if p.tok.kind == .lcbr {
p.inside_fn = true
stmts = p.parse_block_no_scope(true)
p.inside_fn = false
2020-02-04 09:54:15 +01:00
}
if !no_body && are_args_type_only {
p.error_with_pos('functions with type only args can not have bodies', body_start_pos)
return ast.FnDecl{
scope: 0
}
}
// if no_body && !name.starts_with('C.') {
// p.error_with_pos('did you mean C.$name instead of $name', start_pos)
// }
fn_decl := ast.FnDecl{
2020-01-02 08:30:15 +01:00
name: name
mod: p.mod
2020-01-02 08:30:15 +01:00
stmts: stmts
return_type: return_type
2020-09-27 03:32:56 +02:00
params: params
is_manualfree: is_manualfree
2020-03-05 16:13:14 +01:00
is_deprecated: is_deprecated
2020-08-24 09:04:50 +02:00
is_direct_arr: is_direct_arr
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
}
generic_params: generic_params
receiver_pos: receiver_pos
2020-02-18 20:20:15 +01:00
is_method: is_method
method_type_pos: rec_type_pos
method_idx: type_sym_method_idx
2020-02-18 20:20:15 +01:00
rec_mut: rec_mut
language: language
2020-03-06 16:31:40 +01:00
no_body: no_body
pos: start_pos.extend_with_last_line(end_pos, p.prev_tok.line_nr)
body_pos: body_start_pos
2020-05-04 13:32:25 +02:00
file: p.file_name
is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
attrs: p.attrs
scope: p.scope
2020-01-02 08:30:15 +01:00
}
p.close_scope()
return fn_decl
2020-01-02 08:30:15 +01:00
}
2020-02-11 13:03:10 +01:00
fn (mut p Parser) parse_generic_params() []ast.GenericParam {
mut param_names := []string{}
if p.tok.kind != .lt {
return []ast.GenericParam{}
}
p.check(.lt)
mut first_done := false
mut count := 0
for p.tok.kind !in [.gt, .eof] {
if first_done {
p.check(.comma)
}
name := p.tok.lit
if name.len > 0 && !name[0].is_capital() {
p.error('generic parameter needs to be uppercase')
}
if name.len > 1 {
p.error('generic parameter name needs to be exactly one char')
}
if !util.is_generic_type_name(p.tok.lit) {
p.error('`$p.tok.lit` is a reserved name and cannot be used for generics')
}
if name in param_names {
p.error('duplicated generic parameter `$name`')
}
if count > 8 {
p.error('cannot have more than 9 generic parameters')
}
p.check(.name)
param_names << name
first_done = true
count++
}
p.check(.gt)
return param_names.map(ast.GenericParam{it})
}
// is_generic_name returns true if the current token is a generic name.
fn (p Parser) is_generic_name() bool {
return p.tok.kind == .name && util.is_generic_type_name(p.tok.lit)
}
fn (mut p Parser) anon_fn() ast.AnonFn {
2021-01-21 11:01:40 +01:00
pos := p.tok.position()
2020-04-17 21:59:19 +02:00
p.check(.key_fn)
if p.pref.is_script && p.tok.kind == .name {
p.error_with_pos('function declarations in script mode should be before all script statements',
p.tok.position())
return ast.AnonFn{}
}
2020-04-22 13:11:58 +02:00
p.open_scope()
p.scope.detached_from_parent = true
2020-04-17 21:59:19 +02:00
// TODO generics
args, _, is_variadic := p.fn_args()
2020-04-17 21:59:19 +02:00
for arg in args {
p.scope.register(ast.Var{
2020-04-17 21:59:19 +02:00
name: arg.name
typ: arg.typ
is_mut: arg.is_mut
pos: arg.pos
is_used: true
is_arg: true
2020-04-17 21:59:19 +02:00
})
}
mut same_line := p.tok.line_nr == p.prev_tok.line_nr
mut return_type := table.void_type
// lpar: multiple return types
if same_line {
if p.tok.kind.is_start_of_type() {
return_type = p.parse_type()
} else if p.tok.kind != .lcbr {
p.error_with_pos('expected return type, not $p.tok for anonymous function',
p.tok.position())
}
2020-04-17 21:59:19 +02:00
}
mut stmts := []ast.Stmt{}
2020-04-17 21:59:19 +02:00
no_body := p.tok.kind != .lcbr
same_line = p.tok.line_nr == p.prev_tok.line_nr
if no_body && same_line {
p.error_with_pos('unexpected `$p.tok.kind` after anonymous function signature, expecting `{`',
p.tok.position())
}
2020-04-17 21:59:19 +02:00
if p.tok.kind == .lcbr {
stmts = p.parse_block_no_scope(false)
2020-04-17 21:59:19 +02:00
}
2020-04-22 13:11:58 +02:00
p.close_scope()
mut func := table.Fn{
2020-09-27 03:32:56 +02:00
params: args
2020-04-17 21:59:19 +02:00
is_variadic: is_variadic
return_type: return_type
}
name := 'anon_${p.tok.pos}_${p.table.fn_type_signature(func)}'
func.name = name
idx := p.table.find_or_register_fn_type(p.mod, func, true, false)
2020-04-17 21:59:19 +02:00
typ := table.new_type(idx)
2020-04-25 17:49:16 +02:00
// name := p.table.get_type_name(typ)
2020-04-17 21:59:19 +02:00
return ast.AnonFn{
decl: ast.FnDecl{
name: name
mod: p.mod
2020-04-17 21:59:19 +02:00
stmts: stmts
return_type: return_type
2020-09-27 03:32:56 +02:00
params: args
2020-04-17 21:59:19 +02:00
is_variadic: is_variadic
is_method: false
is_anon: true
no_body: no_body
2021-01-21 11:01:40 +01:00
pos: pos.extend(p.prev_tok.position())
file: p.file_name
scope: p.scope
2020-04-17 21:59:19 +02:00
}
typ: typ
}
}
2020-06-06 12:28:03 +02:00
// part of fn declaration
2020-09-09 13:21:11 +02:00
fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
2020-02-11 13:21:41 +01:00
p.check(.lpar)
2020-09-09 13:21:11 +02:00
mut args := []table.Param{}
mut is_variadic := false
2020-02-11 13:03:10 +01:00
// `int, int, string` (no names, just types)
argname := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() {
p.prepend_mod(p.tok.lit)
} else {
p.tok.lit
}
types_only := p.tok.kind in [.amp, .ellipsis, .key_fn]
|| (p.peek_tok.kind == .comma && p.table.known_type(argname))
|| p.peek_tok.kind == .dot|| p.peek_tok.kind == .rpar
// TODO copy pasta, merge 2 branches
2020-02-11 13:03:10 +01:00
if types_only {
// p.warn('types only')
mut arg_no := 1
2020-02-11 13:03:10 +01:00
for p.tok.kind != .rpar {
2020-12-10 10:56:08 +01:00
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.tok.position())
return []table.Param{}, false, false
}
is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic
is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
if is_mut {
p.next()
}
2020-02-11 13:03:10 +01:00
if p.tok.kind == .ellipsis {
p.next()
2020-02-11 13:03:10 +01:00
is_variadic = true
}
pos := p.tok.position()
mut arg_type := p.parse_type()
2020-12-10 10:56:08 +01:00
if arg_type == 0 {
// error is added in parse_type
return []table.Param{}, false, false
}
if is_mut {
if !arg_type.has_flag(.generic) {
if is_shared {
p.check_fn_shared_arguments(arg_type, pos)
} else if is_atomic {
p.check_fn_atomic_arguments(arg_type, pos)
} else {
p.check_fn_mutable_arguments(arg_type, pos)
}
} else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic`or `shared`', pos)
return []table.Param{}, false, false
}
// if arg_type.is_ptr() {
// p.error('cannot mut')
// }
// arg_type = arg_type.to_ptr()
arg_type = arg_type.set_nr_muls(1)
if is_shared {
arg_type = arg_type.set_flag(.shared_f)
}
if is_atomic {
arg_type = arg_type.set_flag(.atomic_f)
}
}
2020-02-29 09:04:47 +01:00
if is_variadic {
arg_type = table.new_type(p.table.find_or_register_array(arg_type)).set_flag(.variadic)
2020-02-29 09:04:47 +01:00
}
2020-12-10 10:56:08 +01:00
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.prev_tok.position())
return []table.Param{}, false, false
}
2020-02-11 13:03:10 +01:00
if p.tok.kind == .comma {
if is_variadic {
2020-06-03 10:36:52 +02:00
p.error_with_pos('cannot use ...(variadic) with non-final parameter no $arg_no',
pos)
return []table.Param{}, false, false
2020-02-11 13:03:10 +01:00
}
p.next()
}
2020-09-09 13:21:11 +02:00
args << table.Param{
pos: pos
name: ''
is_mut: is_mut
2020-02-11 13:03:10 +01:00
typ: arg_type
}
arg_no++
if arg_no > 1024 {
p.error_with_pos('too many args', pos)
return []table.Param{}, false, false
}
2020-02-11 13:03:10 +01:00
}
} else {
2020-02-11 13:03:10 +01:00
for p.tok.kind != .rpar {
2020-12-10 10:56:08 +01:00
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.tok.position())
return []table.Param{}, false, false
}
is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic
mut is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
2020-05-06 12:13:19 +02:00
if is_mut {
p.next()
}
mut arg_pos := [p.tok.position()]
mut arg_names := [p.check_name()]
mut type_pos := [p.tok.position()]
2020-02-11 13:03:10 +01:00
// `a, b, c int`
for p.tok.kind == .comma {
if !p.pref.is_fmt {
p.warn(
'`fn f(x, y Type)` syntax has been deprecated and will soon be removed. ' +
'Use `fn f(x Type, y Type)` instead. You can run `v fmt -w "$p.scanner.file_path"` to automatically fix your code.')
}
p.next()
arg_pos << p.tok.position()
2020-02-11 13:03:10 +01:00
arg_names << p.check_name()
type_pos << p.tok.position()
2020-02-11 13:03:10 +01:00
}
2020-05-06 12:13:19 +02:00
if p.tok.kind == .key_mut {
// TODO remove old syntax
p.warn_with_pos('use `mut f Foo` instead of `f mut Foo`', p.tok.position())
2020-05-06 12:13:19 +02:00
is_mut = true
}
2020-02-11 13:03:10 +01:00
if p.tok.kind == .ellipsis {
p.next()
2020-02-11 13:03:10 +01:00
is_variadic = true
}
pos := p.tok.position()
mut typ := p.parse_type()
2020-12-10 10:56:08 +01:00
if typ == 0 {
// error is added in parse_type
return []table.Param{}, false, false
}
if is_mut {
if !typ.has_flag(.generic) {
if is_shared {
p.check_fn_shared_arguments(typ, pos)
} else if is_atomic {
p.check_fn_atomic_arguments(typ, pos)
} else {
p.check_fn_mutable_arguments(typ, pos)
}
} else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic` or `shared`',
pos)
return []table.Param{}, false, false
}
typ = typ.set_nr_muls(1)
if is_shared {
typ = typ.set_flag(.shared_f)
}
if is_atomic {
typ = typ.set_flag(.atomic_f)
}
}
2020-02-29 09:04:47 +01:00
if is_variadic {
typ = table.new_type(p.table.find_or_register_array(typ)).set_flag(.variadic)
2020-02-29 09:04:47 +01:00
}
for i, arg_name in arg_names {
2020-09-09 13:21:11 +02:00
args << table.Param{
pos: arg_pos[i]
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
type_pos: type_pos[i]
2020-02-11 13:03:10 +01:00
}
// if typ.typ.kind == .variadic && p.tok.kind == .comma {
if is_variadic && p.tok.kind == .comma {
2020-06-03 10:36:52 +02:00
p.error_with_pos('cannot use ...(variadic) with non-final parameter $arg_name',
arg_pos[i])
return []table.Param{}, false, false
2020-02-11 13:03:10 +01:00
}
}
2020-12-10 10:56:08 +01:00
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.prev_tok.position())
return []table.Param{}, false, false
}
2020-02-11 13:03:10 +01:00
if p.tok.kind != .rpar {
p.check(.comma)
}
}
}
p.check(.rpar)
return args, types_only, is_variadic
2020-02-11 13:03:10 +01:00
}
2020-03-27 08:46:54 +01:00
fn (mut p Parser) check_fn_mutable_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ)
if sym.kind !in [.array, .array_fixed, .interface_, .map, .placeholder, .struct_, .sum_type]
&& !typ.is_ptr()&& !typ.is_pointer() {
p.error_with_pos(
'mutable arguments are only allowed for arrays, interfaces, maps, pointers and structs\n' +
2020-06-06 12:28:03 +02:00
'return values instead: `fn foo(mut n $sym.name) {` => `fn foo(n $sym.name) $sym.name {`',
pos)
}
}
fn (mut p Parser) check_fn_shared_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ)
if sym.kind !in [.array, .struct_, .map, .placeholder] && !typ.is_ptr() {
p.error_with_pos('shared arguments are only allowed for arrays, maps, and structs\n',
pos)
}
}
fn (mut p Parser) check_fn_atomic_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ)
if sym.kind !in [.u32, .int, .u64] {
p.error_with_pos('atomic arguments are only allowed for 32/64 bit integers\n' +
'use shared arguments instead: `fn foo(atomic n $sym.name) {` => `fn foo(shared n $sym.name) {`',
pos)
}
}
fn (mut p Parser) fn_redefinition_error(name string) {
2020-08-11 16:26:49 +02:00
if p.pref.translated {
return
}
// Find where this function was already declared
// TODO
/*
for file in p.ast_files {
}
*/
p.table.redefined_fns << name
// p.error('redefinition of function `$name`')
}
fn have_fn_main(stmts []ast.Stmt) bool {
for stmt in stmts {
if stmt is ast.FnDecl {
if stmt.name == 'main.main' && stmt.mod == 'main' {
return true
}
}
}
return false
}