parser: allow re-assigning fns to variables
parent
b0deac6756
commit
b5a1544bf8
|
@ -1728,6 +1728,11 @@ pub fn (mut c Checker) ident(ident mut ast.Ident) table.Type {
|
||||||
info := ident.info as ast.IdentFn
|
info := ident.info as ast.IdentFn
|
||||||
return info.typ
|
return info.typ
|
||||||
} else if ident.kind == .unresolved {
|
} else if ident.kind == .unresolved {
|
||||||
|
// prepend mod to look for fn call or const
|
||||||
|
mut name := ident.name
|
||||||
|
if !name.contains('.') && ident.mod !in ['builtin', 'main'] {
|
||||||
|
name = '${ident.mod}.$ident.name'
|
||||||
|
}
|
||||||
// first use
|
// first use
|
||||||
start_scope := c.file.scope.innermost(ident.pos.pos)
|
start_scope := c.file.scope.innermost(ident.pos.pos)
|
||||||
if obj := start_scope.find(ident.name) {
|
if obj := start_scope.find(ident.name) {
|
||||||
|
@ -1737,6 +1742,17 @@ pub fn (mut c Checker) ident(ident mut ast.Ident) table.Type {
|
||||||
if typ == 0 {
|
if typ == 0 {
|
||||||
typ = c.expr(it.expr)
|
typ = c.expr(it.expr)
|
||||||
}
|
}
|
||||||
|
sym := c.table.get_type_symbol(typ)
|
||||||
|
if sym.info is table.FnType {
|
||||||
|
// anon/local fn assigned to new variable uses this
|
||||||
|
info := sym.info as table.FnType
|
||||||
|
fn_type := table.new_type(c.table.find_or_register_fn_type(info.func, true, true))
|
||||||
|
ident.kind = .function
|
||||||
|
ident.info = ast.IdentFn{
|
||||||
|
typ: fn_type
|
||||||
|
}
|
||||||
|
return fn_type
|
||||||
|
} else {
|
||||||
is_optional := typ.flag_is(.optional)
|
is_optional := typ.flag_is(.optional)
|
||||||
ident.kind = .variable
|
ident.kind = .variable
|
||||||
ident.info = ast.IdentVar{
|
ident.info = ast.IdentVar{
|
||||||
|
@ -1750,14 +1766,10 @@ pub fn (mut c Checker) ident(ident mut ast.Ident) table.Type {
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// prepend mod to look for fn call or const
|
|
||||||
mut name := ident.name
|
|
||||||
if !name.contains('.') && ident.mod !in ['builtin', 'main'] {
|
|
||||||
name = '${ident.mod}.$ident.name'
|
|
||||||
}
|
|
||||||
if obj := c.file.global_scope.find(name) {
|
if obj := c.file.global_scope.find(name) {
|
||||||
match obj {
|
match obj {
|
||||||
ast.GlobalDecl {
|
ast.GlobalDecl {
|
||||||
|
@ -1783,7 +1795,7 @@ pub fn (mut c Checker) ident(ident mut ast.Ident) table.Type {
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Function object (not a call), e.g. `onclick(my_click)`
|
// Non-anon-function object (not a call), e.g. `onclick(my_click)`
|
||||||
if func := c.table.find_fn(name) {
|
if func := c.table.find_fn(name) {
|
||||||
fn_type := table.new_type(c.table.find_or_register_fn_type(func, false, true))
|
fn_type := table.new_type(c.table.find_or_register_fn_type(func, false, true))
|
||||||
ident.name = name
|
ident.name = name
|
||||||
|
|
|
@ -367,7 +367,8 @@ typedef struct {
|
||||||
sym := g.table.get_type_symbol(func.return_type)
|
sym := g.table.get_type_symbol(func.return_type)
|
||||||
is_multi := sym.kind == .multi_return
|
is_multi := sym.kind == .multi_return
|
||||||
is_fn_sig := func.name == ''
|
is_fn_sig := func.name == ''
|
||||||
if !info.has_decl && (!info.is_anon || is_fn_sig) && !is_multi {
|
not_anon := !info.is_anon
|
||||||
|
if !info.has_decl && !is_multi && (not_anon || is_fn_sig) {
|
||||||
fn_name := if func.is_c {
|
fn_name := if func.is_c {
|
||||||
func.name.replace('.', '__')
|
func.name.replace('.', '__')
|
||||||
} else if info.is_anon {
|
} else if info.is_anon {
|
||||||
|
@ -890,8 +891,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
or_stmts = it.or_block.stmts
|
or_stmts = it.or_block.stmts
|
||||||
return_type = it.return_type
|
return_type = it.return_type
|
||||||
}
|
}
|
||||||
ast.AnonFn {
|
|
||||||
// TODO: no buffer fiddling
|
// TODO: no buffer fiddling
|
||||||
|
ast.AnonFn {
|
||||||
if blank_assign {
|
if blank_assign {
|
||||||
g.write('{')
|
g.write('{')
|
||||||
}
|
}
|
||||||
|
@ -908,6 +909,23 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
ast.Ident {
|
||||||
|
if it.info is ast.IdentFn {
|
||||||
|
thing := it.info as ast.IdentFn
|
||||||
|
sym := g.table.get_type_symbol(thing.typ)
|
||||||
|
info := sym.info as table.FnType
|
||||||
|
func := info.func
|
||||||
|
ret_styp := g.typ(func.return_type)
|
||||||
|
g.write('$ret_styp (*$ident.name) (')
|
||||||
|
def_pos := g.definitions.len
|
||||||
|
g.fn_args(func.args, func.is_variadic)
|
||||||
|
g.definitions.go_back(g.definitions.len - def_pos)
|
||||||
|
g.write(') = ')
|
||||||
|
g.expr(*it)
|
||||||
|
g.writeln(';')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
gen_or := is_call && return_type.flag_is(.optional)
|
gen_or := is_call && return_type.flag_is(.optional)
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
// helper
|
||||||
|
fn sqr(x int) int {
|
||||||
|
return x * x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high_fn(f fn(int) int) {
|
||||||
|
x := f(111)
|
||||||
|
println('x == $x')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high_fn_no_ret(f fn(int)) {
|
||||||
|
f(111)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high_fn_array(f fn(a []int) []int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high_fn_multi_return(a int, b fn (c []int, d []string) ([]int, []string)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high_fn_return_single_anon() (fn(int)f32) {
|
||||||
|
_ := 1
|
||||||
|
correct := fn(n int)f32 {
|
||||||
|
return n * n
|
||||||
|
}
|
||||||
|
return correct
|
||||||
|
}
|
||||||
|
fn high_fn_return_multi_anons() (fn(int)f32, fn(int)string) {
|
||||||
|
// parsing trap
|
||||||
|
_ := fn(n int)byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
correct_second := fn(n int)string {
|
||||||
|
return '$n'
|
||||||
|
}
|
||||||
|
correct_first := fn(n int)f32 {
|
||||||
|
return n * n
|
||||||
|
}
|
||||||
|
// parsing trap
|
||||||
|
_ := fn(n int)[]int {
|
||||||
|
return [n]
|
||||||
|
}
|
||||||
|
return correct_first, correct_second
|
||||||
|
}
|
||||||
|
fn high_fn_return_named_fn() (fn(int)int) {
|
||||||
|
return sqr
|
||||||
|
}
|
||||||
|
fn test_high_fn_ret_anons() {
|
||||||
|
param := 13
|
||||||
|
func_sqr1 := high_fn_return_single_anon()
|
||||||
|
assert func_sqr1(param) == param * param
|
||||||
|
|
||||||
|
func_sqr2, func_repr := high_fn_return_multi_anons()
|
||||||
|
assert func_sqr2(param) == (param * param)
|
||||||
|
assert func_repr(param) == '$param'
|
||||||
|
|
||||||
|
top_lvl_sqr := high_fn_return_named_fn()
|
||||||
|
assert top_lvl_sqr(param) == param * param
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high_fn_applier(arg int, func fn(a int)string) string {
|
||||||
|
return func(arg)
|
||||||
|
}
|
||||||
|
fn test_high_fn_applier() {
|
||||||
|
arg := 13
|
||||||
|
expect := '$arg $arg'
|
||||||
|
func := fn (arg int) string {
|
||||||
|
return '$arg $arg'
|
||||||
|
}
|
||||||
|
assert expect == high_fn_applier(arg, func)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_fns() {
|
||||||
|
// no asserts for now, just test function declarations above
|
||||||
|
high_fn(sqr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_anon_fn() {
|
||||||
|
f1 := fn(a int){
|
||||||
|
println('hello from f1')
|
||||||
|
}
|
||||||
|
f1(1)
|
||||||
|
|
||||||
|
f2 := fn(a int) int {
|
||||||
|
println('hello from f2')
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
f2res := f2(1)
|
||||||
|
println('f2res == $f2res')
|
||||||
|
// TODO/FIXME: assert bug? uncomment to see
|
||||||
|
// assert f2res == 10
|
||||||
|
|
||||||
|
high_fn(fn (x int) int {
|
||||||
|
return x + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
high_fn_no_ret(fn (x int) {
|
||||||
|
println('hello $x')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test assigning functions (IdentFn)
|
||||||
|
//
|
||||||
|
|
||||||
|
fn simple_fn1() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simple_fn2(n f32) (int, string) {
|
||||||
|
return 1 + n, "fish"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_assigning_fns() {
|
||||||
|
func1 := simple_fn1
|
||||||
|
assert func1() == 1
|
||||||
|
|
||||||
|
func2 := simple_fn2
|
||||||
|
res2_1, res2_2 := func2(13.0)
|
||||||
|
assert res2_1 == 14.0
|
||||||
|
assert res2_2 == "fish"
|
||||||
|
|
||||||
|
anon_func1 := fn(s string)int {
|
||||||
|
return s.len
|
||||||
|
}
|
||||||
|
func3 := anon_func1
|
||||||
|
res3 := func3("fish")
|
||||||
|
assert res3 == 4
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// End assigning functions (IdentFn)
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -111,107 +111,6 @@ fn test_mut_ptr() {
|
||||||
assert buf[0] == 77
|
assert buf[0] == 77
|
||||||
}
|
}
|
||||||
|
|
||||||
fn high_fn(f fn(int) int) {
|
|
||||||
x := f(111)
|
|
||||||
println('x == $x')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn high_fn_no_ret(f fn(int)) {
|
|
||||||
f(111)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn high_fn_array(f fn(a []int) []int) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn high_fn_multi_return(a int, b fn (c []int, d []string) ([]int, []string)) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn high_fn_return_single_anon() (fn(int)f32) {
|
|
||||||
_ := 1
|
|
||||||
correct := fn(n int)f32 {
|
|
||||||
return n * n
|
|
||||||
}
|
|
||||||
return correct
|
|
||||||
}
|
|
||||||
fn high_fn_return_multi_anons() (fn(int)f32, fn(int)string) {
|
|
||||||
// parsing trap
|
|
||||||
_ := fn(n int)byte {
|
|
||||||
return 0x00
|
|
||||||
}
|
|
||||||
correct_second := fn(n int)string {
|
|
||||||
return '$n'
|
|
||||||
}
|
|
||||||
correct_first := fn(n int)f32 {
|
|
||||||
return n * n
|
|
||||||
}
|
|
||||||
// parsing trap
|
|
||||||
_ := fn(n int)[]int {
|
|
||||||
return [n]
|
|
||||||
}
|
|
||||||
return correct_first, correct_second
|
|
||||||
}
|
|
||||||
fn high_fn_return_named_fn() (fn(int)int) {
|
|
||||||
return sqr
|
|
||||||
}
|
|
||||||
fn test_high_fn_ret_anons() {
|
|
||||||
param := 13
|
|
||||||
func_sqr1 := high_fn_return_single_anon()
|
|
||||||
assert func_sqr1(param) == param * param
|
|
||||||
|
|
||||||
func_sqr2, func_repr := high_fn_return_multi_anons()
|
|
||||||
assert func_sqr2(param) == (param * param)
|
|
||||||
assert func_repr(param) == '$param'
|
|
||||||
|
|
||||||
top_lvl_sqr := high_fn_return_named_fn()
|
|
||||||
assert top_lvl_sqr(param) == param * param
|
|
||||||
}
|
|
||||||
|
|
||||||
fn high_fn_applier(arg int, func fn(a int)string) string {
|
|
||||||
return func(arg)
|
|
||||||
}
|
|
||||||
fn test_high_fn_applier() {
|
|
||||||
arg := 13
|
|
||||||
expect := '$arg $arg'
|
|
||||||
func := fn (arg int) string {
|
|
||||||
return '$arg $arg'
|
|
||||||
}
|
|
||||||
assert expect == high_fn_applier(arg, func)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sqr(x int) int {
|
|
||||||
return x * x
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_fns() {
|
|
||||||
// no asserts for now, just test function declarations above
|
|
||||||
high_fn(sqr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_anon_fn() {
|
|
||||||
f1 := fn(a int){
|
|
||||||
println('hello from f1')
|
|
||||||
}
|
|
||||||
f1(1)
|
|
||||||
|
|
||||||
f2 := fn(a int) int {
|
|
||||||
println('hello from f2')
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
f2res := f2(1)
|
|
||||||
println('f2res == $f2res')
|
|
||||||
// TODO/FIXME: assert bug? uncomment to see
|
|
||||||
// assert f2res == 10
|
|
||||||
|
|
||||||
high_fn(fn (x int) int {
|
|
||||||
return x + 1
|
|
||||||
})
|
|
||||||
|
|
||||||
high_fn_no_ret(fn (x int) {
|
|
||||||
println('hello $x')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_in_bool_fn(v int) bool {
|
fn assert_in_bool_fn(v int) bool {
|
||||||
assert v < 3
|
assert v < 3
|
||||||
|
|
Loading…
Reference in New Issue