Allow side-effect-free expressions in a limited number of cases

refs #6570
This commit is contained in:
Gunnar Beutner 2015-01-08 10:30:34 +01:00
parent 0091c70c41
commit 093be8b5c9
3 changed files with 89 additions and 50 deletions

View File

@ -82,7 +82,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig
%error-verbose
%glr-parser
%parse-param { std::vector<Expression *> *elist }
%parse-param { std::vector<std::pair<Expression *, EItemInfo> > *llist }
%parse-param { ConfigCompiler *context }
%lex-param { void *scanner }
@ -95,6 +95,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig
CombinedSetOp csop;
icinga::TypeSpecifier type;
std::vector<String> *slist;
std::vector<std::pair<Expression *, EItemInfo> > *llist;
std::vector<Expression *> *elist;
std::pair<String, Expression *> *cvitem;
std::map<String, Expression *> *cvlist;
@ -181,11 +182,12 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig
%type <variant> typerulelist
%type <csop> combined_set_op
%type <type> type
%type <elist> statements
%type <elist> lterm_items
%type <elist> lterm_items_inner
%type <llist> statements
%type <llist> lterm_items
%type <llist> lterm_items_inner
%type <expr> rterm
%type <expr> rterm_array
%type <expr> rterm_scope_require_side_effect
%type <expr> rterm_scope
%type <expr> rterm_side_effect
%type <expr> rterm_no_side_effect
@ -228,23 +230,34 @@ int yylex(YYSTYPE *lvalp, YYLTYPE *llocp, void *scanner);
extern int yydebug;
void yyerror(const YYLTYPE *locp, std::vector<Expression *> *, ConfigCompiler *, const char *err)
void yyerror(const YYLTYPE *locp, std::vector<std::pair<Expression *, EItemInfo> > *, ConfigCompiler *, const char *err)
{
BOOST_THROW_EXCEPTION(ScriptError(err, *locp));
}
int yyparse(std::vector<Expression *> *elist, ConfigCompiler *context);
int yyparse(std::vector<std::pair<Expression *, EItemInfo> > *llist, ConfigCompiler *context);
Expression *ConfigCompiler::Compile(void)
{
std::vector<Expression *> elist;
std::vector<std::pair<Expression *, EItemInfo> > llist;
//yydebug = 1;
if (yyparse(&elist, this) != 0)
if (yyparse(&llist, this) != 0)
return NULL;
DictExpression *expr = new DictExpression(elist);
std::vector<Expression *> dlist;
typedef std::pair<Expression *, EItemInfo> EListItem;
int num = 0;
BOOST_FOREACH(const EListItem& litem, llist) {
if (!litem.second.SideEffect && num != llist.size() - 1) {
yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
}
dlist.push_back(litem.first);
num++;
}
DictExpression *expr = new DictExpression(dlist);
expr->MakeInline();
return expr;
}
@ -256,7 +269,7 @@ Expression *ConfigCompiler::Compile(void)
%%
script: statements
{
elist->swap(*$1);
llist->swap(*$1);
delete $1;
}
;
@ -273,48 +286,47 @@ statements: newlines lterm_items
lterm_items: /* empty */
{
$$ = new std::vector<Expression *>();
$$ = new std::vector<std::pair<Expression *, EItemInfo> >();
}
| lterm_items_inner
{
$$ = $1;
}
| lterm_items_inner ','
{
$$ = $1;
}
| lterm_items_inner ',' newlines
{
$$ = $1;
}
| lterm_items_inner ';'
{
$$ = $1;
}
| lterm_items_inner ';' newlines
{
$$ = $1;
}
| lterm_items_inner newlines
{
$$ = $1;
}
| lterm_items_inner sep
;
lterm_items_inner: lterm %dprec 2
{
$$ = new std::vector<Expression *>();
$$->push_back($1);
$$ = new std::vector<std::pair<Expression *, EItemInfo> >();
EItemInfo info = { true, @1 };
$$->push_back(std::make_pair($1, info));
}
| rterm_no_side_effect
{
$$ = new std::vector<std::pair<Expression *, EItemInfo> >();
EItemInfo info = { false, @1 };
$$->push_back(std::make_pair($1, info));
}
| lterm_items_inner sep lterm %dprec 1
{
if ($1)
$$ = $1;
else
$$ = new std::vector<Expression *>();
$$ = new std::vector<std::pair<Expression *, EItemInfo> >();
if ($3)
$$->push_back($3);
if ($3) {
EItemInfo info = { true, @3 };
$$->push_back(std::make_pair($3, info));
}
}
| lterm_items_inner sep rterm_no_side_effect %dprec 1
{
if ($1)
$$ = $1;
else
$$ = new std::vector<std::pair<Expression *, EItemInfo> >();
if ($3) {
EItemInfo info = { false, @3 };
$$->push_back(std::make_pair($3, info));
}
}
;
@ -439,7 +451,7 @@ object:
context->m_Assign.push(NULL);
context->m_Ignore.push(NULL);
}
object_declaration identifier rterm use_specifier rterm_scope
object_declaration identifier rterm use_specifier rterm_scope_require_side_effect
{
context->m_ObjectAssign.pop();
@ -609,7 +621,7 @@ lterm: type
{
$$ = $1;
}
| T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' rterm_scope
| T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' rterm_scope_require_side_effect
{
DictExpression *aexpr = dynamic_cast<DictExpression *>($9);
aexpr->MakeInline();
@ -618,7 +630,7 @@ lterm: type
free($3);
free($5);
}
| T_FOR '(' identifier T_IN rterm ')' rterm_scope
| T_FOR '(' identifier T_IN rterm ')' rterm_scope_require_side_effect
{
DictExpression *aexpr = dynamic_cast<DictExpression *>($7);
aexpr->MakeInline();
@ -658,10 +670,6 @@ lterm: type
{
$$ = $1;
}
| rterm_no_side_effect
{
yyerror(&@1, NULL, NULL, "Value computed is not used.");
}
;
rterm_items: /* empty */
@ -710,10 +718,35 @@ rterm_array: '[' newlines rterm_items ']'
}
;
rterm_scope_require_side_effect: '{' statements '}'
{
std::vector<Expression *> dlist;
typedef std::pair<Expression *, EItemInfo> EListItem;
int num = 0;
BOOST_FOREACH(const EListItem& litem, *$2) {
if (!litem.second.SideEffect)
yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
dlist.push_back(litem.first);
num++;
}
delete $2;
$$ = new DictExpression(dlist, DebugInfoRange(@1, @3));
}
;
rterm_scope: '{' statements '}'
{
$$ = new DictExpression(*$2, DebugInfoRange(@1, @3));
std::vector<Expression *> dlist;
typedef std::pair<Expression *, EItemInfo> EListItem;
int num = 0;
BOOST_FOREACH(const EListItem& litem, *$2) {
if (!litem.second.SideEffect && num != $2->size() - 1)
yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
dlist.push_back(litem.first);
num++;
}
delete $2;
$$ = new DictExpression(dlist, DebugInfoRange(@1, @3));
}
;
@ -814,7 +847,7 @@ rterm_no_side_effect: T_STRING
$$ = new GetScopeExpression(ScopeThis);
}
| rterm_array
| rterm_scope
| rterm_scope_require_side_effect
{
Expression *expr = $1;
BindToScope(expr, ScopeCurrent);
@ -949,7 +982,7 @@ apply:
context->m_FVVar.push("");
context->m_FTerm.push(NULL);
}
T_APPLY identifier optional_rterm apply_for_specifier target_type_specifier use_specifier rterm_scope
T_APPLY identifier optional_rterm apply_for_specifier target_type_specifier use_specifier rterm_scope_require_side_effect
{
context->m_Apply.pop();

View File

@ -59,6 +59,12 @@ struct CompilerDebugInfo
}
};
struct EItemInfo
{
bool SideEffect;
CompilerDebugInfo DebugInfo;
};
/**
* The configuration compiler can be used to compile a configuration file
* into a number of configuration items.

View File

@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(simple)
delete expr;
expr = ConfigCompiler::CompileText("<test>", "{ 3\n\n5 }");
BOOST_CHECK(expr->Evaluate(frame) != Empty);
BOOST_CHECK_THROW(expr->Evaluate(frame), ScriptError);
delete expr;
expr = ConfigCompiler::CompileText("<test>", "1 + 3");