ast, cgen, checker, parser: support method arguments in comptime $for (#9208)
parent
8de6511056
commit
f26d2f02b7
|
@ -1184,6 +1184,7 @@ pub mut:
|
||||||
sym table.TypeSymbol
|
sym table.TypeSymbol
|
||||||
result_type table.Type
|
result_type table.Type
|
||||||
env_value string
|
env_value string
|
||||||
|
args []CallArg
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct None {
|
pub struct None {
|
||||||
|
|
|
@ -4072,15 +4072,9 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) table.Type {
|
||||||
return rtyp
|
return rtyp
|
||||||
}
|
}
|
||||||
if node.method_name == 'method' {
|
if node.method_name == 'method' {
|
||||||
if node.args_var.len > 0 {
|
for i, arg in node.args {
|
||||||
v := node.scope.find_var(node.args_var) or {
|
// check each arg expression
|
||||||
c.error('unknown identifier `$node.args_var`', node.method_pos)
|
node.args[i].typ = c.expr(arg.expr)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// assume string for now
|
// assume string for now
|
||||||
return table.string_type
|
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{}
|
8 | s1 := S1{}
|
||||||
9 | $for method in S1.methods {
|
9 | $for method in S1.methods {
|
||||||
10 | s1.$method(wrong)
|
10 | s1.$method(wrong)
|
||||||
| ~~~~~~
|
| ~~~~~
|
||||||
11 | arg := 7
|
11 | }
|
||||||
12 | s1.$method(arg)
|
12 | }
|
||||||
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 | }
|
|
||||||
|
|
|
@ -8,7 +8,5 @@ fn test_methods_arg() {
|
||||||
s1 := S1{}
|
s1 := S1{}
|
||||||
$for method in S1.methods {
|
$for method in S1.methods {
|
||||||
s1.$method(wrong)
|
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 {
|
} else if node.is_env {
|
||||||
f.write("\$env('$node.args_var')")
|
f.write("\$env('$node.args_var')")
|
||||||
} else {
|
} else {
|
||||||
method_expr := if node.has_parens {
|
inner_args := if node.args_var != '' {
|
||||||
'(${node.method_name}($node.args_var))'
|
node.args_var
|
||||||
} else {
|
} 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')
|
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) {
|
fn handle_conn<T>(mut app T) {
|
||||||
mut vars := []string{cap: 123}
|
|
||||||
vars << 'abc'
|
|
||||||
vars << 'def'
|
|
||||||
$for method in T.methods {
|
$for method in T.methods {
|
||||||
$if method.return_type is Result {
|
$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 {
|
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}(')
|
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)
|
g.expr(node.left)
|
||||||
if m.params.len > 1 {
|
if m.params.len > 1 {
|
||||||
g.write(', ')
|
g.write(', ')
|
||||||
|
@ -87,15 +114,25 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.params[i].typ.is_int() || m.params[i].typ.idx() == table.bool_type_idx {
|
if i - 1 < node.args.len - 1 {
|
||||||
// Gets the type name and cast the string to the type with the string_<type> function
|
g.expr(node.args[i - 1].expr)
|
||||||
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 {
|
|
||||||
g.write(', ')
|
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')
|
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)
|
p.mark_var_as_used(method_name)
|
||||||
// `app.$action()` (`action` is a string)
|
// `app.$action()` (`action` is a string)
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
mut args_var := ''
|
args := p.call_args()
|
||||||
if p.tok.kind == .name {
|
|
||||||
args_var = p.tok.lit
|
|
||||||
p.mark_var_as_used(args_var)
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
p.check(.rpar)
|
p.check(.rpar)
|
||||||
if p.tok.kind == .key_orelse {
|
if p.tok.kind == .key_orelse {
|
||||||
p.check(.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_name: method_name
|
||||||
method_pos: method_pos
|
method_pos: method_pos
|
||||||
scope: p.scope
|
scope: p.scope
|
||||||
args_var: args_var
|
args_var: ''
|
||||||
|
args: args
|
||||||
pos: start_pos.extend(p.prev_tok.position())
|
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() {
|
fn test_comptime_method_names() {
|
||||||
|
mut num_methods := 0
|
||||||
$for method in TestStruct.methods {
|
$for method in TestStruct.methods {
|
||||||
if method.name == 'test' {
|
if method.name == 'one_arg' {
|
||||||
args := method.args
|
assert method.args[0].name == 'a1'
|
||||||
assert args[0].name == 'arg1'
|
num_methods++
|
||||||
assert args[1].name == 'arg2'
|
} else if method.name == 'two_args' {
|
||||||
assert args[2].name == 'arg3'
|
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.
|
// should be called first.
|
||||||
if !route_path.contains('/:') && url_words == route_words {
|
if !route_path.contains('/:') && url_words == route_words {
|
||||||
// We found a match
|
// We found a match
|
||||||
app.$method(method_args)
|
app.$method()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if url_words.len == 0 && route_words == ['index'] && method.name == 'index' {
|
if url_words.len == 0 && route_words == ['index'] && method.name == 'index' {
|
||||||
app.$method(method_args)
|
app.$method()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue