From 73073cd954de95dd5c657ffdec63c49c7867e715 Mon Sep 17 00:00:00 2001 From: Tim Basel Date: Fri, 17 Apr 2020 21:59:19 +0200 Subject: [PATCH] parser: anonymous functions (part 1) --- vlib/v/ast/ast.v | 12 ++++++++-- vlib/v/checker/checker.v | 3 +++ vlib/v/gen/cgen.v | 37 ++++++++++++++++++++++++++-- vlib/v/parser/fn.v | 52 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index ce2c6135bd..1e8abcd80e 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -10,7 +10,7 @@ import ( pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl -pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral | FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr | AssignExpr | PrefixExpr | IndexExpr | RangeExpr | MatchExpr | CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr | ConcatExpr | Type | AsCast | TypeOf | StringInterLiteral +pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral | FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr | AssignExpr | PrefixExpr | IndexExpr | RangeExpr | MatchExpr | CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr | ConcatExpr | Type | AsCast | TypeOf | StringInterLiteral | AnonFn pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt | HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt | Comment | AssertStmt | UnsafeStmt | GoStmt | Block | InterfaceDecl @@ -184,6 +184,13 @@ pub: alias string } +pub struct AnonFn { +pub: + decl FnDecl +mut: + typ table.Type +} + pub struct FnDecl { pub: name string @@ -193,11 +200,12 @@ pub: is_deprecated bool is_pub bool is_variadic bool + is_anon bool receiver Field is_method bool rec_mut bool // is receiver mutable is_c bool - is_js bool + is_js bool no_body bool // just a definition `fn C.malloc()` is_builtin bool // this function is defined in builtin/strconv pos token.Position diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 36cbea6228..111a2305cc 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1198,6 +1198,9 @@ pub fn (c mut Checker) expr(node ast.Expr) table.Type { it.expr_type = c.expr(it.expr) return table.string_type } + ast.AnonFn { + return it.typ + } else { tnode := typeof(node) if tnode != 'unknown v.ast.Expr' { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index a6c0d9452d..52cb010f67 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -248,8 +248,14 @@ pub fn (var g Gen) write_typedef_types() { .function { info := typ.info as table.FnType func := info.func - if !info.has_decl && !info.is_anon { - fn_name := if func.is_c { func.name.replace('.', '__') } else { c_name(func.name) } + if !info.has_decl { + fn_name := if func.is_c { + func.name.replace('.', '__') + } else if info.is_anon { + typ.name + } else { + c_name(func.name) + } g.definitions.write('typedef ${g.typ(func.return_type)} (*$fn_name)(') for i, arg in func.args { g.definitions.write(g.typ(arg.typ)) @@ -1085,6 +1091,33 @@ fn (var g Gen) expr(node ast.Expr) { ast.TypeOf { g.typeof_expr(it) } + ast.AnonFn { + sym := g.table.get_type_symbol(it.typ) + func := it.decl + + // TODO: Fix hack and write function implementation directly to definitions + pos := g.out.len + type_name := g.typ(func.return_type) + g.write('$type_name ${sym.name}_impl(') + g.fn_args(func.args, func.is_variadic) + g.writeln(') {') + g.stmts(func.stmts) + if g.autofree { + g.free_scope_vars(func.pos.pos - 1) + } + if g.defer_stmts.len > 0 { + g.write_defer_stmts() + } + g.out.writeln('}') + g.defer_stmts = [] + g.fn_decl = 0 + + fn_body := g.out.after(pos) + g.definitions.write(fn_body) + g.out.go_back(fn_body.len) + + g.out.write('&${sym.name}_impl') + } else { // #printf("node=%d\n", node.typ); println(term.red('cgen.expr(): bad node ' + typeof(node))) diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 90e3a1d82d..2b519216bf 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -234,6 +234,58 @@ fn (var p Parser) fn_decl() ast.FnDecl { } } +fn (var p Parser) anon_fn() ast.AnonFn { + pos := p.tok.position() + p.open_scope() + p.check(.key_fn) + + // TODO generics + + args, is_variadic := p.fn_args() + for arg in args { + p.scope.register(arg.name, ast.Var{ + name: arg.name + typ: arg.typ + }) + } + + var return_type := table.void_type + if p.tok.kind.is_start_of_type() { + return_type = p.parse_type() + } + + var stmts := []ast.Stmt + no_body := p.tok.kind != .lcbr + if p.tok.kind == .lcbr { + stmts = p.parse_block() + } + p.close_scope() + + func := table.Fn{ + args: args + is_variadic: is_variadic + return_type: return_type + } + idx := p.table.find_or_register_fn_type(func, false) + typ := table.new_type(idx) + name := p.table.get_type_name(typ) + + return ast.AnonFn{ + decl: ast.FnDecl{ + name: name + stmts: stmts + return_type: return_type + args: args + is_variadic: is_variadic + is_method: false + is_anon: true + no_body: no_body + pos: pos + } + typ: typ + } +} + fn (var p Parser) fn_args() ([]table.Arg, bool) { p.check(.lpar) var args := []table.Arg