compiler/lang: add variadic function args support
parent
5fb3c0e3a8
commit
f7c8e923c0
102
compiler/fn.v
102
compiler/fn.v
|
@ -737,7 +737,23 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
||||||
if is_mut {
|
if is_mut {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
mut typ := p.get_type()
|
mut typ := ''
|
||||||
|
// variadic arg
|
||||||
|
if p.tok == .ellipsis {
|
||||||
|
p.check(.ellipsis)
|
||||||
|
if p.tok == .rpar {
|
||||||
|
p.error('you must provide a type for vargs: eg `...string`. multiple types `...` are not supported yet.')
|
||||||
|
}
|
||||||
|
t := p.get_type()
|
||||||
|
vargs_struct := '_V_FnVargs_$f.name'
|
||||||
|
// register varg struct, incase function is never called
|
||||||
|
p.fn_define_vargs_stuct(f, t, []string)
|
||||||
|
p.cgen.typedefs << 'typedef struct $vargs_struct $vargs_struct;\n'
|
||||||
|
typ = '...$t'
|
||||||
|
} else {
|
||||||
|
typ = p.get_type()
|
||||||
|
}
|
||||||
|
|
||||||
p.check_and_register_used_imported_type(typ)
|
p.check_and_register_used_imported_type(typ)
|
||||||
if is_mut && is_primitive_type(typ) {
|
if is_mut && is_primitive_type(typ) {
|
||||||
p.error('mutable arguments are only allowed for arrays, maps, and structs.' +
|
p.error('mutable arguments are only allowed for arrays, maps, and structs.' +
|
||||||
|
@ -765,9 +781,14 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
||||||
if p.tok == .comma {
|
if p.tok == .comma {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
if p.tok == .dotdot {
|
// unnamed (C definition)
|
||||||
f.args << Var{
|
if p.tok == .ellipsis {
|
||||||
name: '..'
|
if !f.is_c {
|
||||||
|
p.error('variadic argument syntax must be `arg_name ...type` eg `argname ...string`.')
|
||||||
|
}
|
||||||
|
f.args << Var {
|
||||||
|
// name: '...'
|
||||||
|
typ: '...'
|
||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
|
@ -779,6 +800,11 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||||
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
||||||
// C func. # of args is not known
|
// C func. # of args is not known
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
|
mut is_variadic := false
|
||||||
|
if f.args.len > 0 {
|
||||||
|
last_arg := f.args.last()
|
||||||
|
is_variadic = last_arg.typ.starts_with('...')
|
||||||
|
}
|
||||||
if f.is_c {
|
if f.is_c {
|
||||||
for p.tok != .rpar {
|
for p.tok != .rpar {
|
||||||
//C.func(var1, var2.method())
|
//C.func(var1, var2.method())
|
||||||
|
@ -819,7 +845,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Reached the final vararg? Quit
|
// Reached the final vararg? Quit
|
||||||
if i == f.args.len - 1 && arg.name == '..' {
|
if i == f.args.len - 1 && arg.typ.starts_with('...') {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
ph := p.cgen.add_placeholder()
|
ph := p.cgen.add_placeholder()
|
||||||
|
@ -978,32 +1004,23 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||||
// Check for commas
|
// Check for commas
|
||||||
if i < f.args.len - 1 {
|
if i < f.args.len - 1 {
|
||||||
// Handle 0 args passed to varargs
|
// Handle 0 args passed to varargs
|
||||||
is_vararg := i == f.args.len - 2 && f.args[i + 1].name == '..'
|
if p.tok != .comma && !is_variadic {
|
||||||
if p.tok != .comma && !is_vararg {
|
|
||||||
p.error('wrong number of arguments for $i,$arg.name fn `$f.name`: expected $f.args.len, but got less')
|
p.error('wrong number of arguments for $i,$arg.name fn `$f.name`: expected $f.args.len, but got less')
|
||||||
}
|
}
|
||||||
if p.tok == .comma {
|
if p.tok == .comma {
|
||||||
p.fgen(', ')
|
p.fgen(', ')
|
||||||
}
|
}
|
||||||
if !is_vararg {
|
if !is_variadic {
|
||||||
p.next()
|
p.next()
|
||||||
p.gen(',')
|
p.gen(',')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// varargs
|
// varargs
|
||||||
if f.args.len > 0 {
|
if !p.first_pass() && is_variadic {
|
||||||
last_arg := f.args.last()
|
p.fn_gen_caller_vargs(mut f)
|
||||||
if last_arg.name == '..' {
|
|
||||||
for p.tok != .rpar {
|
|
||||||
if p.tok == .comma {
|
|
||||||
p.gen(',')
|
|
||||||
p.check(.comma)
|
|
||||||
}
|
|
||||||
p.bool_expression()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.tok == .comma {
|
if p.tok == .comma {
|
||||||
p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more')
|
p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more')
|
||||||
}
|
}
|
||||||
|
@ -1011,6 +1028,49 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||||
return f // TODO is return f right?
|
return f // TODO is return f right?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fn_define_vargs_stuct(f &Fn, typ string, values []string) {
|
||||||
|
vargs_struct := '_V_FnVargs_$f.name'
|
||||||
|
varg_type := Type{
|
||||||
|
cat: TypeCategory.struct_,
|
||||||
|
name: vargs_struct,
|
||||||
|
mod: p.mod
|
||||||
|
}
|
||||||
|
if values.len > 0 {
|
||||||
|
p.table.rewrite_type(varg_type)
|
||||||
|
p.cgen.gen(',&($vargs_struct){.len=$values.len,.args={'+values.join(',')+'}}')
|
||||||
|
} else {
|
||||||
|
p.table.register_type2(varg_type)
|
||||||
|
}
|
||||||
|
p.table.add_field(vargs_struct, 'len', 'int', false, '', .public)
|
||||||
|
p.table.add_field(vargs_struct, 'args[$values.len]', typ, false, '', .public)
|
||||||
|
for va in p.table.varg_access {
|
||||||
|
if va.fn_name != f.name { continue }
|
||||||
|
if va.index >= values.len {
|
||||||
|
p.error_with_token_index('error accessing variadic arg, index `$va.index` out of range.', va.tok_idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fn_gen_caller_vargs(f mut Fn) {
|
||||||
|
last_arg := f.args.last()
|
||||||
|
varg_def_type := last_arg.typ.right(3)
|
||||||
|
mut varg_values := []string
|
||||||
|
for p.tok != .rpar {
|
||||||
|
if p.tok == .comma {
|
||||||
|
p.check(.comma)
|
||||||
|
}
|
||||||
|
p.cgen.start_tmp()
|
||||||
|
varg_type := p.bool_expression()
|
||||||
|
varg_value := p.cgen.end_tmp()
|
||||||
|
p.check_types(last_arg.typ, varg_type)
|
||||||
|
ref_deref := if last_arg.typ.ends_with('*') && !varg_type.ends_with('*') { '&' }
|
||||||
|
else if !last_arg.typ.ends_with('*') && varg_type.ends_with('*') { '*' }
|
||||||
|
else { '' }
|
||||||
|
varg_values << '$ref_deref$varg_value'
|
||||||
|
}
|
||||||
|
p.fn_define_vargs_stuct(f, varg_def_type, varg_values)
|
||||||
|
}
|
||||||
|
|
||||||
// "fn (int, string) int"
|
// "fn (int, string) int"
|
||||||
fn (f &Fn) typ_str() string {
|
fn (f &Fn) typ_str() string {
|
||||||
mut sb := strings.new_builder(50)
|
mut sb := strings.new_builder(50)
|
||||||
|
@ -1056,8 +1116,8 @@ fn (f &Fn) str_args(table &Table) string {
|
||||||
s += ')'
|
s += ')'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if arg.name == '..' {
|
else if arg.typ.starts_with('...') {
|
||||||
s += '...'
|
s += '_V_FnVargs_$f.name *$arg.name'
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// s += '$arg.typ $arg.name'
|
// s += '$arg.typ $arg.name'
|
||||||
|
|
|
@ -1947,12 +1947,17 @@ fn (p &Parser) fileis(s string) bool {
|
||||||
|
|
||||||
// user.name => `str_typ` is `User`
|
// user.name => `str_typ` is `User`
|
||||||
// user.company.name => `str_typ` is `Company`
|
// user.company.name => `str_typ` is `Company`
|
||||||
fn (p mut Parser) dot(str_typ string, method_ph int) string {
|
fn (p mut Parser) dot(str_typ_ string, method_ph int) string {
|
||||||
//if p.fileis('orm_test') {
|
//if p.fileis('orm_test') {
|
||||||
//println('ORM dot $str_typ')
|
//println('ORM dot $str_typ')
|
||||||
//}
|
//}
|
||||||
|
mut str_typ := str_typ_
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
mut typ := p.find_type(str_typ)
|
is_variadic_arg := str_typ.starts_with('...')
|
||||||
|
if is_variadic_arg {
|
||||||
|
str_typ = str_typ.right(3)
|
||||||
|
}
|
||||||
|
typ := p.find_type(str_typ)
|
||||||
if typ.name.len == 0 {
|
if typ.name.len == 0 {
|
||||||
p.error('dot(): cannot find type `$str_typ`')
|
p.error('dot(): cannot find type `$str_typ`')
|
||||||
}
|
}
|
||||||
|
@ -1968,6 +1973,14 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
|
||||||
//}
|
//}
|
||||||
has_field := p.table.type_has_field(typ, p.table.var_cgen_name(field_name))
|
has_field := p.table.type_has_field(typ, p.table.var_cgen_name(field_name))
|
||||||
mut has_method := p.table.type_has_method(typ, field_name)
|
mut has_method := p.table.type_has_method(typ, field_name)
|
||||||
|
if is_variadic_arg {
|
||||||
|
if field_name != 'len' {
|
||||||
|
p.error('the only field you can access on variadic args is `.len`')
|
||||||
|
}
|
||||||
|
p.gen('->$field_name')
|
||||||
|
p.next()
|
||||||
|
return 'int'
|
||||||
|
}
|
||||||
// generate `.str()`
|
// generate `.str()`
|
||||||
if !has_method && field_name == 'str' && typ.name.starts_with('array_') {
|
if !has_method && field_name == 'str' && typ.name.starts_with('array_') {
|
||||||
p.gen_array_str(typ)
|
p.gen_array_str(typ)
|
||||||
|
@ -2086,6 +2099,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||||
//println('index expr typ=$typ')
|
//println('index expr typ=$typ')
|
||||||
//println(v.name)
|
//println(v.name)
|
||||||
//}
|
//}
|
||||||
|
is_variadic_arg := typ.starts_with('...')
|
||||||
is_map := typ.starts_with('map_')
|
is_map := typ.starts_with('map_')
|
||||||
is_str := typ == 'string'
|
is_str := typ == 'string'
|
||||||
is_arr0 := typ.starts_with('array_')
|
is_arr0 := typ.starts_with('array_')
|
||||||
|
@ -2095,7 +2109,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||||
mut close_bracket := false
|
mut close_bracket := false
|
||||||
if is_indexer {
|
if is_indexer {
|
||||||
is_fixed_arr := typ[0] == `[`
|
is_fixed_arr := typ[0] == `[`
|
||||||
if !is_str && !is_arr && !is_map && !is_ptr && !is_fixed_arr {
|
if !is_str && !is_arr && !is_map && !is_ptr && !is_fixed_arr && !is_variadic_arg {
|
||||||
p.error('Cant [] non-array/string/map. Got type "$typ"')
|
p.error('Cant [] non-array/string/map. Got type "$typ"')
|
||||||
}
|
}
|
||||||
p.check(.lsbr)
|
p.check(.lsbr)
|
||||||
|
@ -2125,7 +2139,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||||
p.gen('[')
|
p.gen('[')
|
||||||
close_bracket = true
|
close_bracket = true
|
||||||
}
|
}
|
||||||
else if is_ptr {
|
else if is_ptr && !is_variadic_arg {
|
||||||
// typ = 'byte'
|
// typ = 'byte'
|
||||||
typ = typ.replace('*', '')
|
typ = typ.replace('*', '')
|
||||||
// modify(mut []string) fix
|
// modify(mut []string) fix
|
||||||
|
@ -2177,6 +2191,27 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||||
}
|
}
|
||||||
p.expr_var = v
|
p.expr_var = v
|
||||||
}
|
}
|
||||||
|
// accessing variadiac args
|
||||||
|
if !p.first_pass() && is_variadic_arg {
|
||||||
|
typ = typ.right(3)
|
||||||
|
if p.calling_c {
|
||||||
|
p.error('you cannot currently pass varg to a C function.')
|
||||||
|
}
|
||||||
|
if !is_indexer {
|
||||||
|
p.error('You must use array access syntax for variadic arguments.')
|
||||||
|
}
|
||||||
|
varg_type := typ.right(3)
|
||||||
|
l := p.cgen.cur_line.trim_space()
|
||||||
|
index_val := l.right(l.last_index(' ')).trim_space()
|
||||||
|
p.cgen.resetln(l.left(fn_ph))
|
||||||
|
p.table.varg_access << VargAccess{
|
||||||
|
fn_name: p.cur_fn.name,
|
||||||
|
tok_idx: p.token_idx,
|
||||||
|
index: index_val.int()
|
||||||
|
}
|
||||||
|
p.cgen.set_placeholder(fn_ph, '${v.name}->args[$index_val]')
|
||||||
|
return typ
|
||||||
|
}
|
||||||
// TODO move this from index_expr()
|
// TODO move this from index_expr()
|
||||||
// TODO if p.tok in ...
|
// TODO if p.tok in ...
|
||||||
if (p.tok == .assign && !p.is_sql) || p.tok.is_assign() {
|
if (p.tok == .assign && !p.is_sql) || p.tok.is_assign() {
|
||||||
|
|
|
@ -428,6 +428,10 @@ fn (s mut Scanner) scan() ScanRes {
|
||||||
case `.`:
|
case `.`:
|
||||||
if nextc == `.` {
|
if nextc == `.` {
|
||||||
s.pos++
|
s.pos++
|
||||||
|
if s.text[s.pos+1] == `.` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(.ellipsis, '')
|
||||||
|
}
|
||||||
return scan_res(.dotdot, '')
|
return scan_res(.dotdot, '')
|
||||||
}
|
}
|
||||||
return scan_res(.dot, '')
|
return scan_res(.dot, '')
|
||||||
|
|
|
@ -20,9 +20,15 @@ mut:
|
||||||
cflags []CFlag // ['-framework Cocoa', '-lglfw3']
|
cflags []CFlag // ['-framework Cocoa', '-lglfw3']
|
||||||
fn_cnt int //atomic
|
fn_cnt int //atomic
|
||||||
obfuscate bool
|
obfuscate bool
|
||||||
|
varg_access []VargAccess
|
||||||
//names []Name
|
//names []Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct VargAccess {
|
||||||
|
fn_name string
|
||||||
|
tok_idx int
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
enum NameCategory {
|
enum NameCategory {
|
||||||
|
@ -331,6 +337,10 @@ fn (t mut Table) register_fn(new_fn Fn) {
|
||||||
|
|
||||||
fn (table &Table) known_type(typ_ string) bool {
|
fn (table &Table) known_type(typ_ string) bool {
|
||||||
mut typ := typ_
|
mut typ := typ_
|
||||||
|
// vararg
|
||||||
|
if typ.starts_with('...') && typ.len > 3 {
|
||||||
|
typ = typ.right(3)
|
||||||
|
}
|
||||||
// 'byte*' => look up 'byte', but don't mess up fns
|
// 'byte*' => look up 'byte', but don't mess up fns
|
||||||
if typ.ends_with('*') && !typ.contains(' ') {
|
if typ.ends_with('*') && !typ.contains(' ') {
|
||||||
typ = typ.left(typ.len - 1)
|
typ = typ.left(typ.len - 1)
|
||||||
|
@ -558,6 +568,13 @@ fn (p mut Parser) _check_types(got_, expected_ string, throw bool) bool {
|
||||||
if p.pref.translated {
|
if p.pref.translated {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// variadic
|
||||||
|
if expected.starts_with('...') {
|
||||||
|
expected = expected.right(3)
|
||||||
|
}
|
||||||
|
if got.starts_with('...') {
|
||||||
|
got = got.right(3)
|
||||||
|
}
|
||||||
// Allow ints to be used as floats
|
// Allow ints to be used as floats
|
||||||
if got == 'int' && expected == 'f32' {
|
if got == 'int' && expected == 'f32' {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -56,13 +56,6 @@ type actionf_p1 fn (voidptr)
|
||||||
|
|
||||||
type actionf_p2 fn (voidptr, voidptr)
|
type actionf_p2 fn (voidptr, voidptr)
|
||||||
|
|
||||||
fn myprint(s string, ..) {
|
|
||||||
println('my print')
|
|
||||||
println('// comment')
|
|
||||||
println('/* comment */')
|
|
||||||
println('/* /* comment */ */')
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn modify_array(a mut []int) {
|
fn modify_array(a mut []int) {
|
||||||
a[0] = 10
|
a[0] = 10
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
struct VaTestGroup {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variadic_test_a(name string, groups ...VaTestGroup) {
|
||||||
|
assert groups.len == 2
|
||||||
|
assert groups[0].name == 'users'
|
||||||
|
assert groups[1].name == 'admins'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_fn_variadic() {
|
||||||
|
group1 := VaTestGroup{name: 'users'}
|
||||||
|
group2 := VaTestGroup{name: 'admins'}
|
||||||
|
variadic_test_a('joe', group1, group2)
|
||||||
|
}
|
|
@ -68,6 +68,7 @@ enum Token {
|
||||||
nl
|
nl
|
||||||
dot
|
dot
|
||||||
dotdot
|
dotdot
|
||||||
|
ellipsis
|
||||||
// keywords
|
// keywords
|
||||||
keyword_beg
|
keyword_beg
|
||||||
key_as
|
key_as
|
||||||
|
@ -150,6 +151,7 @@ fn build_token_str() []string {
|
||||||
s[Token.not] = '!'
|
s[Token.not] = '!'
|
||||||
s[Token.dot] = '.'
|
s[Token.dot] = '.'
|
||||||
s[Token.dotdot] = '..'
|
s[Token.dotdot] = '..'
|
||||||
|
s[Token.ellipsis] = '...'
|
||||||
s[Token.comma] = ','
|
s[Token.comma] = ','
|
||||||
//s[Token.at] = '@'
|
//s[Token.at] = '@'
|
||||||
s[Token.semicolon] = ';'
|
s[Token.semicolon] = ';'
|
||||||
|
|
Loading…
Reference in New Issue