From 8bfd4197023fd5c8a57323928604a5411d651387 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 2 Aug 2018 10:17:04 +0200 Subject: [PATCH] Implement references --- doc/17-language-reference.md | 12 ++++++++ lib/base/CMakeLists.txt | 1 + lib/base/reference-script.cpp | 52 ++++++++++++++++++++++++++++++++ lib/base/reference.cpp | 55 +++++++++++++++++++++++++++++++++ lib/base/reference.hpp | 57 +++++++++++++++++++++++++++++++++++ lib/config/config_parser.yy | 9 ++++++ lib/config/expression.cpp | 42 ++++++++++++++++++++++++++ lib/config/expression.hpp | 23 ++++++++++++++ 8 files changed, 251 insertions(+) create mode 100644 lib/base/reference-script.cpp create mode 100644 lib/base/reference.cpp create mode 100644 lib/base/reference.hpp diff --git a/doc/17-language-reference.md b/doc/17-language-reference.md index 9bd31d6b7..285a9fcb9 100644 --- a/doc/17-language-reference.md +++ b/doc/17-language-reference.md @@ -168,6 +168,8 @@ Operator | Precedence | Examples (Result) | Descript ~ | 2 | ~true (false) | Bitwise negation of the operand + | 2 | +3 | Unary plus - | 2 | -3 | Unary minus +& | 2 | &var (reference to 'var') | Reference operator +* | 2 | *var | Indirection operator * | 3 | 5m * 10 (3000) | Multiplies two numbers / | 3 | 5m / 5 (60) | Divides two numbers % | 3 | 17 % 12 (5) | Remainder after division @@ -191,6 +193,16 @@ in | 7 | "foo" in [ "foo", "bar" ] (true) | Element = | 12 | a = 3 | Assignment => | 15 | x => x * x (function with arg x) | Lambda, for loop +### References + +A reference to a value can be obtained using the `&` operator. The `*` operator can be used +to dereference a reference: + + var value = "Hello!" + var p = &value /* p refers to value */ + *p = "Hi!" + log(value) // Prints "Hi!" because the variable was changed + ### Function Calls Functions can be called using the `()` operator: diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index c02001ccc..d86f676d2 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -62,6 +62,7 @@ set(base_SOURCES perfdatavalue.cpp perfdatavalue.hpp perfdatavalue-ti.hpp primitivetype.cpp primitivetype.hpp process.cpp process.hpp + reference.cpp reference.hpp reference-script.cpp registry.hpp ringbuffer.cpp ringbuffer.hpp scriptframe.cpp scriptframe.hpp diff --git a/lib/base/reference-script.cpp b/lib/base/reference-script.cpp new file mode 100644 index 000000000..7254333aa --- /dev/null +++ b/lib/base/reference-script.cpp @@ -0,0 +1,52 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "base/reference.hpp" +#include "base/function.hpp" +#include "base/functionwrapper.hpp" +#include "base/scriptframe.hpp" +#include "base/exception.hpp" + +using namespace icinga; + +static void ReferenceSet(const Value& value) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Reference::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + self->Set(value); +} + +static Value ReferenceGet() +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Reference::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + return self->Get(); +} + +Object::Ptr Reference::GetPrototype() +{ + static Dictionary::Ptr prototype = new Dictionary({ + { "set", new Function("Reference#set", ReferenceSet, { "value" }) }, + { "get", new Function("Reference#get", ReferenceGet, {}, true) }, + }); + + return prototype; +} diff --git a/lib/base/reference.cpp b/lib/base/reference.cpp new file mode 100644 index 000000000..53f6e9e2e --- /dev/null +++ b/lib/base/reference.cpp @@ -0,0 +1,55 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "base/reference.hpp" +#include "base/debug.hpp" +#include "base/primitivetype.hpp" +#include "base/dictionary.hpp" +#include "base/configwriter.hpp" +#include "base/convert.hpp" +#include "base/exception.hpp" + +using namespace icinga; + +REGISTER_PRIMITIVE_TYPE_NOINST(Reference, Object, Reference::GetPrototype()); + +Reference::Reference(const Object::Ptr& parent, const String& index) + : m_Parent(parent), m_Index(index) +{ +} + +Value Reference::Get() const +{ + return m_Parent->GetFieldByName(m_Index, true, DebugInfo()); +} + +void Reference::Set(const Value& value) +{ + m_Parent->SetFieldByName(m_Index, value, DebugInfo()); +} + +Object::Ptr Reference::GetParent() const +{ + return m_Parent; +} + +String Reference::GetIndex() const +{ + return m_Index; +} diff --git a/lib/base/reference.hpp b/lib/base/reference.hpp new file mode 100644 index 000000000..347a81db6 --- /dev/null +++ b/lib/base/reference.hpp @@ -0,0 +1,57 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef REFERENCE_H +#define REFERENCE_H + +#include "base/i2-base.hpp" +#include "base/objectlock.hpp" +#include "base/value.hpp" + +namespace icinga +{ + +/** + * A reference. + * + * @ingroup base + */ +class Reference final : public Object +{ +public: + DECLARE_OBJECT(Reference); + + Reference(const Object::Ptr& parent, const String& index); + + Value Get() const; + void Set(const Value& value); + + Object::Ptr GetParent() const; + String GetIndex() const; + + static Object::Ptr GetPrototype(); + +private: + Object::Ptr m_Parent; + String m_Index; +}; + +} + +#endif /* REFERENCE_H */ diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 1a47c6932..497df8a38 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -227,6 +227,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %left T_PLUS T_MINUS %left T_MULTIPLY T_DIVIDE_OP T_MODULO %left UNARY_MINUS UNARY_PLUS +%right REF_OP DEREF_OP %right '!' '~' %left '.' '(' '[' %left T_VAR T_THIS T_GLOBALS T_LOCALS @@ -881,6 +882,14 @@ rterm_no_side_effect_no_dict: T_STRING $$ = new VariableExpression(*$1, @1); delete $1; } + | T_MULTIPLY rterm %prec DEREF_OP + { + $$ = new DerefExpression(std::unique_ptr($2), @$); + } + | T_BINARY_AND rterm %prec REF_OP + { + $$ = new RefExpression(std::unique_ptr($2), @$); + } | '!' rterm { $$ = new LogicalNegateExpression(std::unique_ptr($2), @$); diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 938deb69b..94a966375 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -28,6 +28,7 @@ #include "base/exception.hpp" #include "base/scriptglobal.hpp" #include "base/loader.hpp" +#include "base/reference.hpp" #include #include @@ -156,6 +157,47 @@ bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value return true; } +ExpressionResult RefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + Value parent; + String index; + + if (!m_Operand->GetReference(frame, false, &parent, &index, &dhint)) + BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression.", m_DebugInfo)); + + if (!parent.IsObject()) + BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression because parent is not an object.", m_DebugInfo)); + + return new Reference(parent, index); +} + +ExpressionResult DerefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand = m_Operand->Evaluate(frame); + CHECK_RESULT(operand); + + Object::Ptr obj = operand.GetValue(); + Reference::Ptr ref = dynamic_pointer_cast(obj); + + if (!ref) + BOOST_THROW_EXCEPTION(ScriptError("Invalid reference specified.", GetDebugInfo())); + + return ref->Get(); +} + +bool DerefExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const +{ + ExpressionResult operand = m_Operand->Evaluate(frame); + if (operand.GetCode() != ResultOK) + return false; + + Reference::Ptr ref = operand.GetValue(); + + *parent = ref->GetParent(); + *index = ref->GetIndex(); + return true; +} + ExpressionResult NegateExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { ExpressionResult operand = m_Operand->Evaluate(frame); diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index ce3f438e6..ff25519b7 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -324,6 +324,29 @@ private: friend void BindToScope(std::unique_ptr& expr, ScopeSpecifier scopeSpec); }; +class DerefExpression final : public UnaryExpression +{ +public: + DerefExpression(std::unique_ptr operand, const DebugInfo& debugInfo = DebugInfo()) + : UnaryExpression(std::move(operand), debugInfo) + { } + +protected: + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; + bool GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const override; +}; + +class RefExpression final : public UnaryExpression +{ +public: + RefExpression(std::unique_ptr operand, const DebugInfo& debugInfo = DebugInfo()) + : UnaryExpression(std::move(operand), debugInfo) + { } + +protected: + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; +}; + class NegateExpression final : public UnaryExpression { public: