Implement references

This commit is contained in:
Gunnar Beutner 2018-08-02 10:17:04 +02:00
parent 33492420f3
commit 8bfd419702
8 changed files with 251 additions and 0 deletions

View 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:

View 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

View File

@ -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;
}

55
lib/base/reference.cpp Normal file
View File

@ -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;
}

57
lib/base/reference.hpp Normal file
View File

@ -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 */

View 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), @$);

View 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);

View 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: