Allow "object" and "apply" in AST expressions.

Refs #5870
This commit is contained in:
Gunnar Beutner 2014-03-30 15:04:53 +02:00
parent 3fcb93fd41
commit 397fee13ff
12 changed files with 242 additions and 66 deletions

View File

@ -20,31 +20,21 @@
library "methods"
template CheckCommand "icinga-check-command" {
methods = {
execute = "IcingaCheck"
}
methods.execute = "IcingaCheck"
}
template CheckCommand "cluster-check-command" {
methods = {
execute = "ClusterCheck"
}
methods.execute = "ClusterCheck"
}
template CheckCommand "plugin-check-command" {
methods = {
execute = "PluginCheck"
}
methods.execute = "PluginCheck"
}
template NotificationCommand "plugin-notification-command" {
methods = {
execute = "PluginNotification"
}
methods.execute = "PluginNotification"
}
template EventCommand "plugin-event-command" {
methods = {
execute = "PluginEvent"
}
methods.execute = "PluginEvent"
}

View File

@ -18,6 +18,7 @@
******************************************************************************/
#include "base/scriptfunction.h"
#include "base/scriptvariable.h"
#include "base/registry.h"
#include "base/singleton.h"
@ -32,12 +33,31 @@ Value ScriptFunction::Invoke(const std::vector<Value>& arguments)
return m_Callback(arguments);
}
RegisterFunctionHelper::RegisterFunctionHelper(const String& name, const ScriptFunction::Callback& function)
ScriptFunction::Ptr ScriptFunction::GetByName(const String& name)
{
return ScriptFunctionRegistry::GetInstance()->GetItem(name);
}
void ScriptFunction::Register(const String& name, const ScriptFunction::Callback& function)
{
ScriptVariable::Ptr sv = ScriptVariable::Set(name, name);
sv->SetConstant(true);
ScriptFunction::Ptr func = make_shared<ScriptFunction>(function);
ScriptFunctionRegistry::GetInstance()->Register(name, func);
}
void ScriptFunction::Unregister(const String& name)
{
ScriptVariable::Unregister(name);
ScriptFunctionRegistry::GetInstance()->Unregister(name);
}
RegisterFunctionHelper::RegisterFunctionHelper(const String& name, const ScriptFunction::Callback& function)
{
ScriptFunction::Register(name, function);
}
ScriptFunctionRegistry *ScriptFunctionRegistry::GetInstance(void)
{
return Singleton<ScriptFunctionRegistry>::GetInstance();

View File

@ -47,6 +47,10 @@ public:
Value Invoke(const std::vector<Value>& arguments);
static ScriptFunction::Ptr GetByName(const String& name);
static void Register(const String& name, const ScriptFunction::Callback& function);
static void Unregister(const String& name);
private:
Callback m_Callback;
};

View File

@ -31,7 +31,7 @@ ScriptInterpreter::ScriptInterpreter(const Script::Ptr&)
ScriptInterpreter::~ScriptInterpreter(void)
{
BOOST_FOREACH(const String& function, m_SubscribedFunctions) {
ScriptFunctionRegistry::GetInstance()->Unregister(function);
ScriptFunction::Unregister(function);
}
}
@ -42,7 +42,7 @@ void ScriptInterpreter::SubscribeFunction(const String& name)
m_SubscribedFunctions.insert(name);
ScriptFunction::Ptr sf = make_shared<ScriptFunction>(boost::bind(&ScriptInterpreter::ProcessCall, this, name, _1));
ScriptFunctionRegistry::GetInstance()->Register(name, sf);
ScriptFunction::Register(name, sf);
}
void ScriptInterpreter::UnsubscribeFunction(const String& name)
@ -50,5 +50,5 @@ void ScriptInterpreter::UnsubscribeFunction(const String& name)
ObjectLock olock(this);
m_SubscribedFunctions.erase(name);
ScriptFunctionRegistry::GetInstance()->Unregister(name);
ScriptFunction::Unregister(name);
}

View File

@ -81,6 +81,11 @@ ScriptVariable::Ptr ScriptVariable::Set(const String& name, const Value& value,
return sv;
}
void ScriptVariable::Unregister(const String& name)
{
ScriptVariableRegistry::GetInstance()->Unregister(name);
}
ScriptVariableRegistry *ScriptVariableRegistry::GetInstance(void)
{
return Singleton<ScriptVariableRegistry>::GetInstance();

View File

@ -47,6 +47,7 @@ public:
Value GetData(void) const;
static ScriptVariable::Ptr GetByName(const String& name);
static void Unregister(const String& name);
static Value Get(const String& name);
static ScriptVariable::Ptr Set(const String& name, const Value& value, bool overwrite = true, bool make_const = false);

View File

@ -20,6 +20,8 @@
#include "config/aexpression.h"
#include "config/configerror.h"
#include "config/configitem.h"
#include "config/configitembuilder.h"
#include "config/applyrule.h"
#include "base/array.h"
#include "base/serializer.h"
#include "base/context.h"
@ -246,8 +248,8 @@ Value AExpression::OpLogicalOr(const AExpression *expr, const Dictionary::Ptr& l
Value AExpression::OpFunctionCall(const AExpression *expr, const Dictionary::Ptr& locals)
{
String funcName = expr->m_Operand1;
ScriptFunction::Ptr func = ScriptFunctionRegistry::GetInstance()->GetItem(funcName);
String funcName = expr->EvaluateOperand1(locals);
ScriptFunction::Ptr func = ScriptFunction::GetByName(funcName);
if (!func)
BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
@ -455,3 +457,85 @@ Value AExpression::OpImport(const AExpression *expr, const Dictionary::Ptr& loca
return Empty;
}
Value AExpression::FunctionWrapper(const std::vector<Value>& arguments, const Array::Ptr& funcargs, const AExpression::Ptr& expr, const Dictionary::Ptr& scope)
{
if (arguments.size() < funcargs->GetLength())
BOOST_THROW_EXCEPTION(ConfigError("Too few arguments for function"));
Dictionary::Ptr locals = make_shared<Dictionary>();
locals->Set("__parent", scope);
for (int i = 0; i < std::min(arguments.size(), funcargs->GetLength()); i++)
locals->Set(funcargs->Get(i), arguments[i]);
return expr->Evaluate(locals);
}
Value AExpression::OpFunction(const AExpression* expr, const Dictionary::Ptr& locals)
{
Array::Ptr left = expr->m_Operand1;
AExpression::Ptr aexpr = left->Get(1);
String name = left->Get(0);
if (name.IsEmpty())
name = "__lambda" + Utility::NewUniqueID();
Array::Ptr funcargs = expr->m_Operand2;
ScriptFunction::Callback callback = boost::bind(&AExpression::FunctionWrapper, _1, funcargs, aexpr, locals);
ScriptFunction::Register(name, callback);
return name;
}
Value AExpression::OpApply(const AExpression* expr, const Dictionary::Ptr& locals)
{
Array::Ptr left = expr->m_Operand1;
AExpression::Ptr exprl = expr->m_Operand2;
String type = left->Get(0);
AExpression::Ptr aname = left->Get(1);
AExpression::Ptr filter = left->Get(2);
String name = aname->Evaluate(locals);
ApplyRule::AddRule(type, name, exprl, filter, expr->m_DebugInfo, locals);
return Empty;
}
Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& locals)
{
Array::Ptr left = expr->m_Operand1;
AExpression::Ptr exprl = expr->m_Operand2;
bool abstract = left->Get(0);
String type = left->Get(1);
AExpression::Ptr aname = left->Get(2);
String name = aname->Evaluate(locals);
ConfigItemBuilder::Ptr item = make_shared<ConfigItemBuilder>(expr->m_DebugInfo);
ConfigItem::Ptr oldItem = ConfigItem::GetObject(type, name);
if (oldItem) {
std::ostringstream msgbuf;
msgbuf << "Object '" << name << "' of type '" << type << "' re-defined: " << expr->m_DebugInfo << "; previous definition: " << oldItem->GetDebugInfo();
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(expr->m_DebugInfo));
}
item->SetType(type);
if (name.FindFirstOf("!") != String::NPos) {
std::ostringstream msgbuf;
msgbuf << "Name for object '" << name << "' of type '" << type << "' is invalid: Object names may not contain '!'";
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(expr->m_DebugInfo));
}
item->SetName(name);
item->AddExpression(exprl);
item->SetAbstract(abstract);
item->SetScope(locals);
item->Compile()->Register();
return Empty;
}

View File

@ -79,6 +79,9 @@ public:
static Value OpSetDivide(const AExpression *expr, const Dictionary::Ptr& locals);
static Value OpIndexer(const AExpression *expr, const Dictionary::Ptr& locals);
static Value OpImport(const AExpression *expr, const Dictionary::Ptr& locals);
static Value OpFunction(const AExpression* expr, const Dictionary::Ptr& locals);
static Value OpApply(const AExpression* expr, const Dictionary::Ptr& locals);
static Value OpObject(const AExpression* expr, const Dictionary::Ptr& locals);
private:
OpCallback m_Operator;
@ -90,6 +93,9 @@ private:
Value EvaluateOperand2(const Dictionary::Ptr& locals) const;
static void DumpOperand(std::ostream& stream, const Value& operand, int indent);
static Value FunctionWrapper(const std::vector<Value>& arguments, const Array::Ptr& funcargs,
const AExpression::Ptr& expr, const Dictionary::Ptr& scope);
};
}

View File

@ -64,7 +64,10 @@ void ApplyRule::AddRule(const String& sourceType, const String& name,
bool ApplyRule::EvaluateFilter(const Dictionary::Ptr& scope) const
{
return m_Filter->Evaluate(scope);
scope->Set("__parent", m_Scope);
bool result = m_Filter->Evaluate(scope);
scope->Remove("__parent");
return result;
}
void ApplyRule::EvaluateRules(void)

View File

@ -224,6 +224,8 @@ where return T_WHERE;
import return T_IMPORT;
assign return T_ASSIGN;
ignore return T_IGNORE;
function return T_FUNCTION;
lambda return T_LAMBDA;
\<\< { yylval->op = &AExpression::OpShiftLeft; return T_SHIFT_LEFT; }
\>\> { yylval->op = &AExpression::OpShiftRight; return T_SHIFT_RIGHT; }
\<= { yylval->op = &AExpression::OpLessThanOrEqual; return T_LESS_THAN_OR_EQUAL; }
@ -234,7 +236,7 @@ ignore return T_IGNORE;
in { yylval->op = &AExpression::OpIn; return T_IN; }
&& { yylval->op = &AExpression::OpLogicalAnd; return T_LOGICAL_AND; }
\|\| { yylval->op = &AExpression::OpLogicalOr; return T_LOGICAL_OR; }
[a-zA-Z_][:a-zA-Z0-9\-_]* { yylval->text = strdup(yytext); return T_IDENTIFIER; }
[a-zA-Z_][a-zA-Z0-9\-_]* { yylval->text = strdup(yytext); return T_IDENTIFIER; }
\<[^\>]*\> { yytext[yyleng-1] = '\0'; yylval->text = strdup(yytext + 1); return T_STRING_ANGLE; }
-?[0-9]+(\.[0-9]+)?ms { yylval->num = strtod(yytext, NULL) / 1000; return T_NUMBER; }
-?[0-9]+(\.[0-9]+)?d { yylval->num = strtod(yytext, NULL) * 60 * 60 * 24; return T_NUMBER; }

View File

@ -152,10 +152,14 @@ static void MakeRBinaryOp(Value** result, AExpression::OpCallback& op, Value *le
%token T_IMPORT "import (T_IMPORT)"
%token T_ASSIGN "assign (T_ASSIGN)"
%token T_IGNORE "ignore (T_IGNORE)"
%token T_FUNCTION "function (T_FUNCTION)"
%token T_LAMBDA "lambda (T_LAMBDA)"
%type <text> identifier
%type <array> rterm_items
%type <array> rterm_items_inner
%type <array> identifier_items
%type <array> identifier_items_inner
%type <array> lterm_items
%type <array> lterm_items_inner
%type <variant> typerulelist
@ -165,6 +169,8 @@ static void MakeRBinaryOp(Value** result, AExpression::OpCallback& op, Value *le
%type <variant> rterm
%type <variant> rterm_scope
%type <variant> lterm
%type <variant> object
%type <variant> apply
%left T_LOGICAL_OR
%left T_LOGICAL_AND
@ -226,7 +232,7 @@ statements: /* empty */
| statements statement
;
statement: object | type | include | include_recursive | library | constant | apply
statement: type | include | include_recursive | library | constant
{ }
| lterm
{
@ -414,49 +420,21 @@ object:
}
object_declaration identifier rterm rterm_scope
{
DebugInfo di = DebugInfoRange(@2, @5);
ConfigItemBuilder::Ptr item = make_shared<ConfigItemBuilder>(di);
Array::Ptr args = make_shared<Array>();
AExpression::Ptr aexpr = static_cast<AExpression::Ptr>(*$4);
args->Add(m_Abstract);
args->Add($3);
free($3);
args->Add(*$4);
delete $4;
String name = aexpr->Evaluate(m_ModuleScope);
ConfigItem::Ptr oldItem = ConfigItem::GetObject($3, name);
if (oldItem) {
std::ostringstream msgbuf;
msgbuf << "Object '" << name << "' of type '" << $3 << "' re-defined: " << di << "; previous definition: " << oldItem->GetDebugInfo();
free($3);
AExpression::Ptr exprl = *$5;
delete $5;
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(di));
}
item->SetType($3);
if (name.FindFirstOf("!") != String::NPos) {
std::ostringstream msgbuf;
msgbuf << "Name for object '" << name << "' of type '" << $3 << "' is invalid: Object names may not contain '!'";
free($3);
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(@4));
}
free($3);
item->SetName(name);
AExpression::Ptr exprl = static_cast<AExpression::Ptr>(*$5);
delete $5;
exprl->MakeInline();
item->AddExpression(exprl);
item->SetAbstract(m_Abstract);
item->SetScope(m_ModuleScope);
item->Compile()->Register();
item.reset();
$$ = new Value(make_shared<AExpression>(&AExpression::OpObject, args, exprl, DebugInfoRange(@2, @5)));
}
;
@ -466,6 +444,38 @@ object_declaration: T_OBJECT
m_Abstract = true;
}
identifier_items: identifier_items_inner
{
$$ = $1;
}
| identifier_items_inner ','
{
$$ = $1;
}
;
identifier_items_inner: /* empty */
{
$$ = new Array();
}
| identifier
{
$$ = new Array();
$$->Add($1);
free($1);
}
| identifier_items_inner ',' identifier
{
if ($1)
$$ = $1;
else
$$ = new Array();
$$->Add($3);
free($3);
}
;
lbinary_op: T_SET
| T_SET_PLUS
| T_SET_MINUS
@ -536,7 +546,7 @@ lterm: identifier lbinary_op rterm
}
| identifier '.' T_IDENTIFIER lbinary_op rterm
{
AExpression::Ptr aindex = make_shared<AExpression>(&AExpression::OpLiteral, $1, @1);
AExpression::Ptr aindex = make_shared<AExpression>(&AExpression::OpLiteral, $3, @3);
AExpression::Ptr subexpr = make_shared<AExpression>($4, aindex, static_cast<AExpression::Ptr>(*$5), DebugInfoRange(@1, @5));
free($3);
delete $5;
@ -578,6 +588,14 @@ lterm: identifier lbinary_op rterm
$$ = new Value(make_shared<AExpression>(&AExpression::OpLiteral, Empty, DebugInfoRange(@1, @3)));
}
| apply
{
$$ = $1;
}
| object
{
$$ = $1;
}
| rterm
{
$$ = $1;
@ -641,10 +659,10 @@ rterm: T_STRING
delete $1;
free($3);
}
| T_IDENTIFIER '(' rterm_items ')'
| rterm '(' rterm_items ')'
{
Array::Ptr arguments = Array::Ptr($3);
$$ = new Value(make_shared<AExpression>(&AExpression::OpFunctionCall, $1, make_shared<AExpression>(&AExpression::OpLiteral, arguments, @3), DebugInfoRange(@1, @4)));
$$ = new Value(make_shared<AExpression>(&AExpression::OpFunctionCall, static_cast<AExpression::Ptr>(*$1), make_shared<AExpression>(&AExpression::OpLiteral, arguments, @3), DebugInfoRange(@1, @4)));
free($1);
}
| T_IDENTIFIER
@ -698,6 +716,44 @@ rterm: T_STRING
| rterm T_MINUS rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
| rterm T_MULTIPLY rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
| rterm T_DIVIDE_OP rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
| T_FUNCTION identifier '(' identifier_items ')' rterm_scope
{
Array::Ptr arr = make_shared<Array>();
arr->Add($2);
free($2);
AExpression::Ptr aexpr = *$6;
delete $6;
aexpr->MakeInline();
arr->Add(aexpr);
$$ = new Value(make_shared<AExpression>(&AExpression::OpFunction, arr, Array::Ptr($4), DebugInfoRange(@1, @6)));
}
| T_FUNCTION '(' identifier_items ')' rterm_scope
{
Array::Ptr arr = make_shared<Array>();
arr->Add(Empty);
AExpression::Ptr aexpr = *$5;
delete $5;
aexpr->MakeInline();
arr->Add(aexpr);
$$ = new Value(make_shared<AExpression>(&AExpression::OpFunction, arr, Array::Ptr($3), DebugInfoRange(@1, @5)));
}
| T_LAMBDA identifier_items ':' rterm
{
Array::Ptr arr = make_shared<Array>();
arr->Add(Empty);
arr->Add(*$4);
delete $4;
$$ = new Value(make_shared<AExpression>(&AExpression::OpFunction, arr, Array::Ptr($2), DebugInfoRange(@1, @4)));
}
;
apply:
@ -714,7 +770,6 @@ apply:
delete $4;
String type = $3;
free($3);
String name = aname->Evaluate(m_ModuleScope);
if (!ApplyRule::IsValidType(type))
BOOST_THROW_EXCEPTION(ConfigError("'apply' cannot be used with type '" + type + "'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
@ -727,7 +782,13 @@ apply:
// assign && !ignore
AExpression::Ptr rex = make_shared<AExpression>(&AExpression::OpLogicalNegate, m_Ignore, DebugInfoRange(@2, @5));
AExpression::Ptr filter = make_shared<AExpression>(&AExpression::OpLogicalAnd, m_Assign, rex, DebugInfoRange(@2, @5));
ApplyRule::AddRule(type, name, exprl, filter, DebugInfoRange(@2, @5), m_ModuleScope);
Array::Ptr args = make_shared<Array>();
args->Add(type);
args->Add(aname);
args->Add(filter);
$$ = new Value(make_shared<AExpression>(&AExpression::OpApply, args, exprl, DebugInfoRange(@2, @5)));
m_Assign.reset();
m_Ignore.reset();

View File

@ -138,7 +138,7 @@ void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary,
String validator = ruleList->GetValidator();
if (!validator.IsEmpty()) {
ScriptFunction::Ptr func = ScriptFunctionRegistry::GetInstance()->GetItem(validator);
ScriptFunction::Ptr func = ScriptFunction::GetByName(validator);
if (!func)
BOOST_THROW_EXCEPTION(std::invalid_argument("Validator function '" + validator + "' does not exist."));
@ -219,7 +219,7 @@ void ConfigType::ValidateArray(const Array::Ptr& array,
String validator = ruleList->GetValidator();
if (!validator.IsEmpty()) {
ScriptFunction::Ptr func = ScriptFunctionRegistry::GetInstance()->GetItem(validator);
ScriptFunction::Ptr func = ScriptFunction::GetByName(validator);
if (!func)
BOOST_THROW_EXCEPTION(std::invalid_argument("Validator function '" + validator + "' does not exist."));