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: