ast, cgen, checker, parser: support method arguments in comptime $for (#9208)
parent
8de6511056
commit
f26d2f02b7
|
@ -1184,6 +1184,7 @@ pub mut:
|
|||
sym table.TypeSymbol
|
||||
result_type table.Type
|
||||
env_value string
|
||||
args []CallArg
|
||||
}
|
||||
|
||||
pub struct None {
|
||||
|
|
|
@ -4072,15 +4072,9 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) table.Type {
|
|||
return rtyp
|
||||
}
|
||||
if node.method_name == 'method' {
|
||||
if node.args_var.len > 0 {
|
||||
v := node.scope.find_var(node.args_var) or {
|
||||
c.error('unknown identifier `$node.args_var`', node.method_pos)
|
||||
return table.void_type
|
||||
}
|
||||
s := c.table.type_to_str(c.expr(v.expr))
|
||||
if s != '[]string' {
|
||||
c.error('expected `[]string`, not s', node.method_pos)
|
||||
}
|
||||
for i, arg in node.args {
|
||||
// check each arg expression
|
||||
node.args[i].typ = c.expr(arg.expr)
|
||||
}
|
||||
// assume string for now
|
||||
return table.string_type
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
vlib/v/checker/tests/comptime_call_method.vv:10:7: error: unknown identifier `wrong`
|
||||
vlib/v/checker/tests/comptime_call_method.vv:10:14: error: undefined ident: `wrong`
|
||||
8 | s1 := S1{}
|
||||
9 | $for method in S1.methods {
|
||||
10 | s1.$method(wrong)
|
||||
| ~~~~~~
|
||||
11 | arg := 7
|
||||
12 | s1.$method(arg)
|
||||
vlib/v/checker/tests/comptime_call_method.vv:12:7: error: expected `[]string`, not s
|
||||
10 | s1.$method(wrong)
|
||||
11 | arg := 7
|
||||
12 | s1.$method(arg)
|
||||
| ~~~~~~
|
||||
13 | }
|
||||
14 | }
|
||||
| ~~~~~
|
||||
11 | }
|
||||
12 | }
|
||||
|
|
|
@ -8,7 +8,5 @@ fn test_methods_arg() {
|
|||
s1 := S1{}
|
||||
$for method in S1.methods {
|
||||
s1.$method(wrong)
|
||||
arg := 7
|
||||
s1.$method(arg)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -986,10 +986,15 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
|
|||
} else if node.is_env {
|
||||
f.write("\$env('$node.args_var')")
|
||||
} else {
|
||||
method_expr := if node.has_parens {
|
||||
'(${node.method_name}($node.args_var))'
|
||||
inner_args := if node.args_var != '' {
|
||||
node.args_var
|
||||
} else {
|
||||
'${node.method_name}($node.args_var)'
|
||||
node.args.map(it.str()).join(', ')
|
||||
}
|
||||
method_expr := if node.has_parens {
|
||||
'(${node.method_name}($inner_args))'
|
||||
} else {
|
||||
'${node.method_name}($inner_args)'
|
||||
}
|
||||
f.write('${node.left}.$$method_expr')
|
||||
}
|
||||
|
|
|
@ -67,12 +67,9 @@ fn (mut a App) my_method(p string) Result {
|
|||
}
|
||||
|
||||
fn handle_conn<T>(mut app T) {
|
||||
mut vars := []string{cap: 123}
|
||||
vars << 'abc'
|
||||
vars << 'def'
|
||||
$for method in T.methods {
|
||||
$if method.return_type is Result {
|
||||
app.$method(vars)
|
||||
app.$method('abc', 'def')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,34 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
|
|||
for val in vals {
|
||||
}
|
||||
*/
|
||||
expand_strs := if node.args.len > 0 && m.params.len - 1 >= node.args.len {
|
||||
arg := node.args[node.args.len - 1]
|
||||
param := m.params[node.args.len]
|
||||
|
||||
arg.expr is ast.Ident && g.table.type_to_str(arg.typ) == '[]string'
|
||||
&& g.table.type_to_str(param.typ) != '[]string'
|
||||
} else {
|
||||
false
|
||||
}
|
||||
// check argument length and types
|
||||
if m.params.len - 1 != node.args.len && !expand_strs {
|
||||
// do not generate anything if the argument lengths don't match
|
||||
g.writeln('/* skipping ${node.sym.name}.$m.name due to mismatched arguments list */')
|
||||
// verror('expected ${m.params.len-1} arguments to method ${node.sym.name}.$m.name, but got $node.args.len')
|
||||
return
|
||||
}
|
||||
// TODO: check argument types
|
||||
g.write('${util.no_dots(node.sym.name)}_${g.comp_for_method}(')
|
||||
|
||||
// try to see if we need to pass a pointer
|
||||
if node.left is ast.Ident {
|
||||
scope := g.file.scope.innermost(node.pos.pos)
|
||||
if v := scope.find_var(node.left.name) {
|
||||
if m.params[0].typ.is_ptr() && !v.typ.is_ptr() {
|
||||
g.write('&')
|
||||
}
|
||||
}
|
||||
}
|
||||
g.expr(node.left)
|
||||
if m.params.len > 1 {
|
||||
g.write(', ')
|
||||
|
@ -87,15 +114,25 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
|
|||
continue
|
||||
}
|
||||
}
|
||||
if m.params[i].typ.is_int() || m.params[i].typ.idx() == table.bool_type_idx {
|
||||
// Gets the type name and cast the string to the type with the string_<type> function
|
||||
type_name := g.table.types[int(m.params[i].typ)].str()
|
||||
g.write('string_${type_name}(((string*)${node.args_var}.data) [${i - 1}])')
|
||||
} else {
|
||||
g.write('((string*)${node.args_var}.data) [${i - 1}] ')
|
||||
}
|
||||
if i < m.params.len - 1 {
|
||||
if i - 1 < node.args.len - 1 {
|
||||
g.expr(node.args[i - 1].expr)
|
||||
g.write(', ')
|
||||
} else if !expand_strs && i == node.args.len {
|
||||
g.expr(node.args[i - 1].expr)
|
||||
break
|
||||
} else {
|
||||
// last argument; try to expand if it's []string
|
||||
idx := i - node.args.len
|
||||
if m.params[i].typ.is_int() || m.params[i].typ.idx() == table.bool_type_idx {
|
||||
// Gets the type name and cast the string to the type with the string_<type> function
|
||||
type_name := g.table.types[int(m.params[i].typ)].str()
|
||||
g.write('string_${type_name}(((string*)${node.args[node.args.len - 1]}.data) [$idx])')
|
||||
} else {
|
||||
g.write('((string*)${node.args[node.args.len - 1]}.data) [$idx] ')
|
||||
}
|
||||
if i < m.params.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
}
|
||||
g.write(' ); // vweb action call with args')
|
||||
|
|
|
@ -305,12 +305,7 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr {
|
|||
p.mark_var_as_used(method_name)
|
||||
// `app.$action()` (`action` is a string)
|
||||
p.check(.lpar)
|
||||
mut args_var := ''
|
||||
if p.tok.kind == .name {
|
||||
args_var = p.tok.lit
|
||||
p.mark_var_as_used(args_var)
|
||||
p.next()
|
||||
}
|
||||
args := p.call_args()
|
||||
p.check(.rpar)
|
||||
if p.tok.kind == .key_orelse {
|
||||
p.check(.key_orelse)
|
||||
|
@ -321,7 +316,8 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr {
|
|||
method_name: method_name
|
||||
method_pos: method_pos
|
||||
scope: p.scope
|
||||
args_var: args_var
|
||||
args_var: ''
|
||||
args: args
|
||||
pos: start_pos.extend(p.prev_tok.position())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,52 @@
|
|||
struct TestStruct {}
|
||||
struct TestStruct {
|
||||
mut:
|
||||
one_arg_called bool
|
||||
two_args_called bool
|
||||
three_args_called bool
|
||||
}
|
||||
|
||||
fn (t TestStruct) test(arg1 string, arg2 string, arg3 string) {}
|
||||
fn (mut t TestStruct) one_arg(a1 string) {
|
||||
t.one_arg_called = true
|
||||
}
|
||||
fn (mut t TestStruct) two_args(a2 string, b2 int) {
|
||||
t.two_args_called = true
|
||||
}
|
||||
fn (mut t TestStruct) three_args(a3 string, b3 int, c3 []string) {
|
||||
t.three_args_called = true
|
||||
}
|
||||
|
||||
fn test_comptime_method_names() {
|
||||
mut num_methods := 0
|
||||
$for method in TestStruct.methods {
|
||||
if method.name == 'test' {
|
||||
args := method.args
|
||||
assert args[0].name == 'arg1'
|
||||
assert args[1].name == 'arg2'
|
||||
assert args[2].name == 'arg3'
|
||||
if method.name == 'one_arg' {
|
||||
assert method.args[0].name == 'a1'
|
||||
num_methods++
|
||||
} else if method.name == 'two_args' {
|
||||
assert method.args[0].name == 'a2'
|
||||
assert method.args[1].name == 'b2'
|
||||
num_methods++
|
||||
} else if method.name == 'three_args' {
|
||||
assert method.args[0].name == 'a3'
|
||||
assert method.args[1].name == 'b3'
|
||||
assert method.args[2].name == 'c3'
|
||||
num_methods++
|
||||
}
|
||||
}
|
||||
assert num_methods == 3
|
||||
}
|
||||
|
||||
fn test_comptime_call_method() {
|
||||
mut t := TestStruct{}
|
||||
$for method in TestStruct.methods {
|
||||
if method.name == 'one_arg' {
|
||||
t.$method('one')
|
||||
} else if method.name == 'two_args' {
|
||||
t.$method('two', 2)
|
||||
} else if method.name == 'three_args' {
|
||||
t.$method('three', 3, ['th' 'ree'])
|
||||
}
|
||||
}
|
||||
assert t.one_arg_called
|
||||
assert t.two_args_called
|
||||
assert t.three_args_called
|
||||
}
|
||||
|
|
|
@ -379,12 +379,12 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
|
|||
// should be called first.
|
||||
if !route_path.contains('/:') && url_words == route_words {
|
||||
// We found a match
|
||||
app.$method(method_args)
|
||||
app.$method()
|
||||
return
|
||||
}
|
||||
|
||||
if url_words.len == 0 && route_words == ['index'] && method.name == 'index' {
|
||||
app.$method(method_args)
|
||||
app.$method()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue