]> granicus.if.org Git - icinga2/commitdiff
Implement references 6521/head
authorGunnar Beutner <gunnar.beutner@icinga.com>
Thu, 2 Aug 2018 08:17:04 +0000 (10:17 +0200)
committerGunnar Beutner <gunnar.beutner@icinga.com>
Tue, 7 Aug 2018 05:44:48 +0000 (07:44 +0200)
doc/17-language-reference.md
lib/base/CMakeLists.txt
lib/base/reference-script.cpp [new file with mode: 0644]
lib/base/reference.cpp [new file with mode: 0644]
lib/base/reference.hpp [new file with mode: 0644]
lib/config/config_parser.yy
lib/config/expression.cpp
lib/config/expression.hpp

index 9bd31d6b791d22f9057e19a3524882bea4965cb4..285a9fcb9876eddfbc94b63988088bc7fee6ad2f 100644 (file)
@@ -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 id="references"></a>
+
+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 <a id="function-calls"></a>
 
 Functions can be called using the `()` operator:
index c02001ccc7704e8c469ad1b2cf111f6773f29d3c..d86f676d242bd5c732f8da8054f8009db3a38896 100644 (file)
@@ -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 (file)
index 0000000..7254333
--- /dev/null
@@ -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<Reference::Ptr>(vframe->Self);
+       REQUIRE_NOT_NULL(self);
+       self->Set(value);
+}
+
+static Value ReferenceGet()
+{
+       ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+       Reference::Ptr self = static_cast<Reference::Ptr>(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 (file)
index 0000000..53f6e9e
--- /dev/null
@@ -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 (file)
index 0000000..347a81d
--- /dev/null
@@ -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 */
index 1a47c69328af216abfc1d06627c209f3f387ef1d..497df8a383366b2ec5ee2788f98881357e86539c 100644 (file)
@@ -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<Expression>($2), @$);
+       }
+       | T_BINARY_AND rterm %prec REF_OP
+       {
+               $$ = new RefExpression(std::unique_ptr<Expression>($2), @$);
+       }
        | '!' rterm
        {
                $$ = new LogicalNegateExpression(std::unique_ptr<Expression>($2), @$);
index 938deb69b5ed64cd4c0eed09f54e407c94d49da6..94a966375ed1dc880164cb4266d1b291a891dafe 100644 (file)
@@ -28,6 +28,7 @@
 #include "base/exception.hpp"
 #include "base/scriptglobal.hpp"
 #include "base/loader.hpp"
+#include "base/reference.hpp"
 #include <boost/exception_ptr.hpp>
 #include <boost/exception/errinfo_nested_exception.hpp>
 
@@ -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<Reference>(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);
index ce3f438e6a2c6b18cdf942180f9e97bb9e362828..ff25519b71407f2c4729942e94f5e6cb7af30937 100644 (file)
@@ -324,6 +324,29 @@ private:
        friend void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec);
 };
 
+class DerefExpression final : public UnaryExpression
+{
+public:
+       DerefExpression(std::unique_ptr<Expression> 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<Expression> 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: