From 5683a03f554c5e8e939c48a859e0eb72f56a5e7d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 7 Jan 2020 11:42:11 +0100 Subject: [PATCH] DSL: allow function()use(this){} and function()use(this=x){} --- doc/17-language-reference.md | 8 ++++++++ lib/base/function.cpp | 7 ++++++- lib/base/function.hpp | 6 +++++- lib/config/config_parser.yy | 8 ++++++++ lib/config/expression.cpp | 2 +- lib/config/expression.hpp | 10 +++++++++- lib/config/vmops.hpp | 10 ++++++++-- 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/doc/17-language-reference.md b/doc/17-language-reference.md index 53bea28a3..efbe48a86 100644 --- a/doc/17-language-reference.md +++ b/doc/17-language-reference.md @@ -1049,6 +1049,14 @@ function MakeHelloFunction(name) { } ``` +The `use` keyword also supports `this`, i.e.: + +``` +(function() use (this = [ "foo", "bar" ]) { + log(this) +})() +``` + ## Conditional Statements ### Conditional Statements: if/else diff --git a/lib/base/function.cpp b/lib/base/function.cpp index f9a261dc0..9bd254d88 100644 --- a/lib/base/function.cpp +++ b/lib/base/function.cpp @@ -10,8 +10,9 @@ using namespace icinga; REGISTER_TYPE_WITH_PROTOTYPE(Function, Function::GetPrototype()); Function::Function(const String& name, Callback function, const std::vector& args, + bool hasClosedThis, Value closedThis, bool side_effect_free, bool deprecated) - : m_Callback(std::move(function)) + : m_Callback(std::move(function)), m_HasClosedThis(hasClosedThis), m_ClosedThis(std::move(closedThis)) { SetName(name, true); SetSideEffectFree(side_effect_free, true); @@ -21,6 +22,10 @@ Function::Function(const String& name, Callback function, const std::vector& arguments) { + if (m_HasClosedThis) { + return InvokeThis(m_ClosedThis, arguments); + } + ScriptFrame frame(false); return m_Callback(arguments); } diff --git a/lib/base/function.hpp b/lib/base/function.hpp index d52a230a1..39e847519 100644 --- a/lib/base/function.hpp +++ b/lib/base/function.hpp @@ -27,8 +27,9 @@ public: template Function(const String& name, F function, const std::vector& args = std::vector(), + bool hasClosedThis = false, Value closedThis = Value(), bool side_effect_free = false, bool deprecated = false) - : Function(name, WrapFunction(function), args, side_effect_free, deprecated) + : Function(name, WrapFunction(function), args, hasClosedThis, std::move(closedThis), side_effect_free, deprecated) { } Value Invoke(const std::vector& arguments = std::vector()); @@ -50,8 +51,11 @@ public: private: Callback m_Callback; + bool m_HasClosedThis; + Value m_ClosedThis; Function(const String& name, Callback function, const std::vector& args, + bool hasClosedThis, Value closedThis, bool side_effect_free, bool deprecated); }; diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 939681e68..bf9494f3c 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -1103,6 +1103,14 @@ use_specifier_item: identifier $$ = new std::pair >(std::move(*$1), std::unique_ptr($3)); delete $1; } + | T_THIS + { + $$ = new std::pair >("this", std::unique_ptr(new GetScopeExpression(ScopeThis))); + } + | T_THIS T_SET rterm + { + $$ = new std::pair >("this", std::unique_ptr($3)); + } ; apply_for_specifier: /* empty */ diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 09b860cde..d13fd4e19 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -904,7 +904,7 @@ ExpressionResult ImportDefaultTemplatesExpression::DoEvaluate(ScriptFrame& frame ExpressionResult FunctionExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { - return VMOps::NewFunction(frame, m_Name, m_Args, m_ClosedVars, m_Expression); + return VMOps::NewFunction(frame, m_Name, m_Args, m_ClosedVars, m_ClosedThis, m_Expression); } ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 644548d28..07f075729 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -814,7 +814,14 @@ public: FunctionExpression(String name, std::vector args, std::map >&& closedVars, std::unique_ptr expression, const DebugInfo& debugInfo = DebugInfo()) : DebuggableExpression(debugInfo), m_Name(std::move(name)), m_Args(std::move(args)), m_ClosedVars(std::move(closedVars)), m_Expression(expression.release()) - { } + { + auto closedThis (m_ClosedVars.find("this")); + + if (closedThis != m_ClosedVars.end()) { + m_ClosedThis = std::move(closedThis->second); + m_ClosedVars.erase(closedThis); + } + } protected: ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; @@ -823,6 +830,7 @@ private: String m_Name; std::vector m_Args; std::map > m_ClosedVars; + std::unique_ptr m_ClosedThis; Expression::Ptr m_Expression; }; diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index ea3098359..2cc33f383 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -91,9 +91,15 @@ public: } static inline Value NewFunction(ScriptFrame& frame, const String& name, const std::vector& argNames, - const std::map >& closedVars, const Expression::Ptr& expression) + const std::map >& closedVars, + const std::unique_ptr& closedThis, const Expression::Ptr& expression) { auto evaluatedClosedVars = EvaluateClosedVars(frame, closedVars); + Value evaluatedClosedThis; + + if (closedThis) { + evaluatedClosedThis = closedThis->Evaluate(frame); + } auto wrapper = [argNames, evaluatedClosedVars, expression](const std::vector& arguments) -> Value { if (arguments.size() < argNames.size()) @@ -112,7 +118,7 @@ public: return expression->Evaluate(*frame); }; - return new Function(name, wrapper, argNames); + return new Function(name, wrapper, argNames, (bool)closedThis, std::move(evaluatedClosedThis)); } static inline Value NewApply(ScriptFrame& frame, const String& type, const String& target, const String& name, const Expression::Ptr& filter,