diff --git a/src/Type.cc b/src/Type.cc index eac80264e8..8c48e0da97 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -622,6 +622,8 @@ FuncType::FuncType(RecordTypePtr arg_args, } prototypes.emplace_back(Prototype{false, "", args, std::move(offsets)}); + + captures = nullptr; } TypePtr FuncType::ShallowClone() @@ -632,6 +634,7 @@ TypePtr FuncType::ShallowClone() f->yield = yield; f->flavor = flavor; f->prototypes = prototypes; + f->captures = captures; return f; } @@ -654,7 +657,15 @@ string FuncType::FlavorString() const } } -FuncType::~FuncType() = default; +FuncType::~FuncType() + { + if ( captures ) + { + for ( auto c : *captures ) + delete c; + delete captures; + } + } int FuncType::MatchesIndex(detail::ListExpr* const index) const { @@ -698,6 +709,11 @@ bool FuncType::CheckArgs(const std::vector& args, return success; } +void FuncType::SetCaptures(std::vector* _captures) + { + captures = _captures; + } + void FuncType::Describe(ODesc* d) const { if ( d->IsReadable() ) diff --git a/src/Type.h b/src/Type.h index 68c33eca7b..31b50daa0f 100644 --- a/src/Type.h +++ b/src/Type.h @@ -10,6 +10,7 @@ #include #include "zeek/Obj.h" +#include "zeek/ID.h" #include "zeek/Attr.h" #include "zeek/ZeekList.h" #include "zeek/IntrusivePtr.h" @@ -540,6 +541,29 @@ public: const std::vector& Prototypes() const { return prototypes; } + /** + * A single lambda "capture" (outer variable used in a lambda's body). + */ + struct Capture { + detail::IDPtr id; + bool deep_copy; + }; + + /** + * Sets this function's set of captures. Only valid for lambdas. + * + * @param captures if non-nil, a list of the lambda's captures + */ + void SetCaptures(std::vector* captures); + + /** + * Returns the captures declared for this function, or nil if none. + * + * @return a vector giving the captures + */ + const std::vector* GetCaptures() const + { return captures; } + protected: friend FuncTypePtr make_intrusive(); @@ -549,6 +573,8 @@ protected: TypePtr yield; FunctionFlavor flavor; std::vector prototypes; + + std::vector* captures; // if nil then no captures specified }; class TypeType final : public Type { diff --git a/src/parse.y b/src/parse.y index b940d3c58d..b7f684007a 100644 --- a/src/parse.y +++ b/src/parse.y @@ -51,9 +51,9 @@ %left '$' '[' ']' '(' ')' TOK_HAS_FIELD TOK_HAS_ATTR %nonassoc TOK_AS TOK_IS -%type opt_no_test opt_no_test_block TOK_PATTERN_END +%type opt_no_test opt_no_test_block TOK_PATTERN_END opt_deep %type TOK_ID TOK_PATTERN_TEXT -%type local_id global_id def_global_id event_id global_or_event_id resolve_id begin_func case_type +%type local_id global_id def_global_id event_id global_or_event_id resolve_id begin_lambda case_type %type local_id_list case_type_list %type init_class %type opt_init @@ -72,6 +72,8 @@ %type case_list %type attr %type attr_list opt_attr +%type capture +%type capture_list opt_captures %{ #include @@ -254,6 +256,8 @@ static bool expr_is_table_type_name(const zeek::detail::Expr* expr) zeek::detail::Attr* attr; std::vector* attr_l; zeek::detail::AttrTag attrtag; + zeek::FuncType::Capture* capture; + std::vector* captures; } %% @@ -532,14 +536,10 @@ expr: $$ = new zeek::detail::FieldAssignExpr($2, {zeek::AdoptRef{}, $4}); } - | '$' TOK_ID func_params '=' + | '$' TOK_ID begin_lambda '=' { func_hdr_location = @1; - auto func_id = zeek::detail::current_scope()->GenerateTemporary("anonymous-function"); - func_id->SetInferReturnType(true); - zeek::detail::begin_func(std::move(func_id), zeek::detail::current_module.c_str(), - zeek::FUNC_FLAVOR_FUNCTION, false, - {zeek::AdoptRef{}, $3}); + $3->SetInferReturnType(true); } lambda_body { @@ -1306,19 +1306,71 @@ lambda_body: ; anonymous_function: - TOK_FUNCTION begin_func lambda_body + TOK_FUNCTION begin_lambda lambda_body { $$ = $3; } ; -begin_func: - func_params +begin_lambda: + opt_captures func_params { auto id = zeek::detail::current_scope()->GenerateTemporary("anonymous-function"); - zeek::detail::begin_func(id, zeek::detail::current_module.c_str(), zeek::FUNC_FLAVOR_FUNCTION, 0, {zeek::AdoptRef{}, $1}); + zeek::detail::begin_func(id, zeek::detail::current_module.c_str(), zeek::FUNC_FLAVOR_FUNCTION, false, {zeek::AdoptRef{}, $2}); + $2->SetCaptures($1); $$ = id.release(); } ; +opt_captures: + '[' capture_list ']' + { $$ = $2; } + | + { $$ = nullptr; } + ; + +capture_list: + capture_list ',' capture + { $1->push_back($3); } + | capture + { + $$ = new std::vector; + $$->push_back($1); + } + ; + +capture: + opt_deep TOK_ID + { + zeek::detail::set_location(@2); + auto id = zeek::detail::lookup_ID($2, + zeek::detail::current_module.c_str()); + + if ( ! id ) + zeek::reporter->Error("no such local identifier: %s", $2); + else if ( id->IsType() ) + { + zeek::reporter->Error("cannot specify type in capture: %s", $2); + id = nullptr; + } + else if ( id->IsGlobal() ) + { + zeek::reporter->Error("cannot specify global in capture: %s", $2); + id = nullptr; + } + + delete [] $2; + + $$ = new zeek::FuncType::Capture; + $$->id = id; + $$->deep_copy = $1; + } + ; + +opt_deep: TOK_COPY + { $$ = true; } + | + { $$ = false; } + ; + func_params: '(' formal_args ')' ':' type { $$ = new zeek::FuncType({zeek::AdoptRef{}, $2}, {zeek::AdoptRef{}, $5}, zeek::FUNC_FLAVOR_FUNCTION); }