]> granicus.if.org Git - icinga2/blobdiff - lib/config/config_parser.yy
Implement more unit tests
[icinga2] / lib / config / config_parser.yy
index f508747a6595cf366bef21d7ab408b0265b3abc7..9ba840a72ee276b1c47fc01125ae2269cf2bae3b 100644 (file)
@@ -1,4 +1,6 @@
 %{
+#define YYDEBUG 1
 /******************************************************************************
  * Icinga 2                                                                   *
  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "i2-config.h"
-#include "config/expression.h"
-#include "config/expressionlist.h"
-#include "config/configitembuilder.h"
-#include "config/configcompiler.h"
-#include "config/configcompilercontext.h"
-#include "config/typerule.h"
-#include "config/typerulelist.h"
-#include "config/aexpression.h"
-#include "config/applyrule.h"
-#include "base/value.h"
-#include "base/utility.h"
-#include "base/array.h"
-#include "base/scriptvariable.h"
-#include "base/exception.h"
-#include "base/dynamictype.h"
+#include "config/i2-config.hpp"
+#include "config/configitembuilder.hpp"
+#include "config/configtype.hpp"
+#include "config/configcompiler.hpp"
+#include "config/configcompilercontext.hpp"
+#include "config/typerule.hpp"
+#include "config/typerulelist.hpp"
+#include "config/expression.hpp"
+#include "config/applyrule.hpp"
+#include "config/objectrule.hpp"
+#include "base/value.hpp"
+#include "base/utility.hpp"
+#include "base/scriptvariable.hpp"
+#include "base/exception.hpp"
+#include "base/dynamictype.hpp"
+#include "base/configerror.hpp"
 #include <sstream>
 #include <stack>
 #include <boost/foreach.hpp>
 
 #define YYLTYPE icinga::DebugInfo
+#define YYERROR_VERBOSE
+
+#define YYLLOC_DEFAULT(Current, Rhs, N)                                        \
+do {                                                                   \
+       if (N) {                                                        \
+               (Current).Path = YYRHSLOC(Rhs, 1).Path;                 \
+               (Current).FirstLine = YYRHSLOC(Rhs, 1).FirstLine;       \
+               (Current).FirstColumn = YYRHSLOC(Rhs, 1).FirstColumn;   \
+               (Current).LastLine = YYRHSLOC(Rhs, N).LastLine;         \
+               (Current).LastColumn = YYRHSLOC(Rhs, N).LastColumn;     \
+       } else {                                                        \
+               (Current).Path = YYRHSLOC(Rhs, 0).Path;                 \
+               (Current).FirstLine = (Current).LastLine =              \
+               YYRHSLOC(Rhs, 0).LastLine;                              \
+               (Current).FirstColumn = (Current).LastColumn =          \
+               YYRHSLOC(Rhs, 0).LastColumn;                            \
+       }                                                               \
+} while (0)
+
+#define YY_LOCATION_PRINT(file, loc)                   \
+do {                                                   \
+       std::ostringstream msgbuf;                      \
+       msgbuf << loc;                                  \
+       std::string str = msgbuf.str();                 \
+       fputs(str.c_str(), file);                       \
+} while (0)
 
 using namespace icinga;
 
+int ignore_newlines = 0;
+
+template<typename T>
+static void MakeRBinaryOp(Expression** result, Expression *left, Expression *right, DebugInfo& diLeft, DebugInfo& diRight)
+{
+       *result = new T(left, right, DebugInfoRange(diLeft, diRight));
+}
+
 %}
 
 %pure-parser
@@ -56,28 +92,29 @@ using namespace icinga;
 %union {
        char *text;
        double num;
+       icinga::Expression *expr;
        icinga::Value *variant;
-       icinga::ExpressionOperator op;
+       CombinedSetOp csop;
        icinga::TypeSpecifier type;
        std::vector<String> *slist;
-       Expression *expr;
-       ExpressionList *exprl;
-       Array *array;
-       Value *aexpr;
+       std::vector<Expression *> *elist;
+       std::pair<String, Expression *> *cvitem;
+       std::map<String, Expression *> *cvlist;
 }
 
+%token T_NEWLINE "new-line"
 %token <text> T_STRING
 %token <text> T_STRING_ANGLE
 %token <num> T_NUMBER
 %token T_NULL
 %token <text> T_IDENTIFIER
-%token <op> T_SET "= (T_SET)"
-%token <op> T_PLUS_EQUAL "+= (T_PLUS_EQUAL)"
-%token <op> T_MINUS_EQUAL "-= (T_MINUS_EQUAL)"
-%token <op> T_MULTIPLY_EQUAL "*= (T_MULTIPLY_EQUAL)"
-%token <op> T_DIVIDE_EQUAL "/= (T_DIVIDE_EQUAL)"
-%token T_VAR "var (T_VAR)"
-%token T_CONST "const (T_CONST)"
+
+%token <csop> T_SET "= (T_SET)"
+%token <csop> T_SET_ADD "+= (T_SET_ADD)"
+%token <csop> T_SET_SUBTRACT "-= (T_SET_SUBTRACT)"
+%token <csop> T_SET_MULTIPLY "*= (T_SET_MULTIPLY)"
+%token <csop> T_SET_DIVIDE "/= (T_SET_DIVIDE)"
+
 %token T_SHIFT_LEFT "<< (T_SHIFT_LEFT)"
 %token T_SHIFT_RIGHT ">> (T_SHIFT_RIGHT)"
 %token T_EQUAL "== (T_EQUAL)"
@@ -86,6 +123,20 @@ using namespace icinga;
 %token T_NOT_IN "!in (T_NOT_IN)"
 %token T_LOGICAL_AND "&& (T_LOGICAL_AND)"
 %token T_LOGICAL_OR "|| (T_LOGICAL_OR)"
+%token T_LESS_THAN_OR_EQUAL "<= (T_LESS_THAN_OR_EQUAL)"
+%token T_GREATER_THAN_OR_EQUAL ">= (T_GREATER_THAN_OR_EQUAL)"
+%token T_PLUS "+ (T_PLUS)"
+%token T_MINUS "- (T_MINUS)"
+%token T_MULTIPLY "* (T_MULTIPLY)"
+%token T_DIVIDE_OP "/ (T_DIVIDE_OP)"
+%token T_BINARY_AND "& (T_BINARY_AND)"
+%token T_BINARY_OR "| (T_BINARY_OR)"
+%token T_LESS_THAN "< (T_LESS_THAN)"
+%token T_GREATER_THAN "> (T_GREATER_THAN)"
+
+%token T_CONST "const (T_CONST)"
+%token T_LOCAL "local (T_LOCAL)"
+%token T_USE "use (T_USE)"
 %token <type> T_TYPE_DICTIONARY "dictionary (T_TYPE_DICTIONARY)"
 %token <type> T_TYPE_ARRAY "array (T_TYPE_ARRAY)"
 %token <type> T_TYPE_NUMBER "number (T_TYPE_NUMBER)"
@@ -103,38 +154,63 @@ using namespace icinga;
 %token T_INCLUDE_RECURSIVE "include_recursive (T_INCLUDE_RECURSIVE)"
 %token T_LIBRARY "library (T_LIBRARY)"
 %token T_INHERITS "inherits (T_INHERITS)"
-%token T_PARTIAL "partial (T_PARTIAL)"
 %token T_APPLY "apply (T_APPLY)"
 %token T_TO "to (T_TO)"
 %token T_WHERE "where (T_WHERE)"
+%token T_IMPORT "import (T_IMPORT)"
+%token T_ASSIGN "assign (T_ASSIGN)"
+%token T_IGNORE "ignore (T_IGNORE)"
+%token T_APPLY_FOR "for (T_APPLY_FOR)"
+%token T_FUNCTION "function (T_FUNCTION)"
+%token T_RETURN "return (T_RETURN)"
+%token T_FOR "for (T_FOR)"
+%token T_SIGNAL "signal (T_SIGNAL)"
+%token T_FOLLOWS "=> (T_FOLLOWS)"
+
 %type <text> identifier
-%type <array> array_items
-%type <array> array_items_inner
-%type <variant> value
-%type <expr> expression
-%type <exprl> expressions
-%type <exprl> expressions_inner
-%type <exprl> expressionlist
+%type <elist> rterm_items
+%type <elist> rterm_items_inner
+%type <slist> identifier_items
+%type <slist> identifier_items_inner
+%type <elist> indexer
+%type <elist> indexer_items
+%type <expr> indexer_item
+%type <elist> lterm_items
+%type <elist> lterm_items_inner
 %type <variant> typerulelist
-%type <op> operator
+%type <csop> combined_set_op
 %type <type> type
-%type <num> partial_specifier
-%type <slist> object_inherits_list
-%type <slist> object_inherits_specifier
-%type <aexpr> aexpression
-%type <num> variable_decl
+%type <expr> rterm
+%type <expr> rterm_array
+%type <expr> rterm_scope
+%type <expr> lterm
+%type <expr> object
+%type <expr> apply
+%type <expr> optional_rterm
+%type <text> target_type_specifier
+%type <cvlist> use_specifier
+%type <cvlist> use_specifier_items
+%type <cvitem> use_specifier_item
+
+%right T_INCLUDE T_INCLUDE_RECURSIVE T_OBJECT T_TEMPLATE T_APPLY T_IMPORT T_ASSIGN T_IGNORE T_WHERE
+%right T_FUNCTION T_SIGNAL T_FOR
 %left T_LOGICAL_OR
 %left T_LOGICAL_AND
-%left T_IN
-%left T_NOT_IN
-%nonassoc T_EQUAL
-%nonassoc T_NOT_EQUAL
-%left '+' '-'
-%left '*' '/'
-%left '&'
-%left '|'
-%right '~'
-%right '!'
+%left T_LOCAL T_RETURN
+%left T_IDENTIFIER
+%left T_SET T_SET_ADD T_SET_SUBTRACT T_SET_MULTIPLY T_SET_DIVIDE
+%left T_BINARY_OR
+%left T_BINARY_AND
+%left T_IN T_NOT_IN
+%nonassoc T_EQUAL T_NOT_EQUAL
+%nonassoc T_LESS_THAN T_LESS_THAN_OR_EQUAL T_GREATER_THAN T_GREATER_THAN_OR_EQUAL
+%left T_SHIFT_LEFT T_SHIFT_RIGHT
+%left T_PLUS T_MINUS
+%left T_MULTIPLY T_DIVIDE_OP
+%right '!' '~'
+%left '.' '(' '['
+%right ';' ','
+%right T_NEWLINE
 %{
 
 int yylex(YYSTYPE *lvalp, YYLTYPE *llocp, void *scanner);
@@ -143,24 +219,61 @@ void yyerror(YYLTYPE *locp, ConfigCompiler *, const char *err)
 {
        std::ostringstream message;
        message << *locp << ": " << err;
-       ConfigCompilerContext::GetInstance()->AddMessage(true, message.str());
+       ConfigCompilerContext::GetInstance()->AddMessage(true, message.str(), *locp);
 }
 
 int yyparse(ConfigCompiler *context);
 
-static std::stack<Array::Ptr> m_Arrays;
-static bool m_Abstract;
+static std::stack<bool> m_Abstract;
 
 static std::stack<TypeRuleList::Ptr> m_RuleLists;
 static ConfigType::Ptr m_Type;
 
-void ConfigCompiler::Compile(void)
+static std::stack<bool> m_Apply;
+static std::stack<bool> m_ObjectAssign;
+static std::stack<bool> m_SeenAssign;
+static std::stack<Expression *> m_Assign;
+static std::stack<Expression *> m_Ignore;
+static std::stack<String> m_FKVar;
+static std::stack<String> m_FVVar;
+static std::stack<Expression *> m_FTerm;
+static std::stack<std::vector<Expression *> > m_Expressions;
+
+Expression *ConfigCompiler::Compile(void)
 {
+       m_Abstract = std::stack<bool>();
+       m_RuleLists = std::stack<TypeRuleList::Ptr>();
+       m_Type.reset();
+       m_Apply = std::stack<bool>();
+       m_ObjectAssign = std::stack<bool>();
+       m_SeenAssign = std::stack<bool>();
+       m_Assign = std::stack<Expression *>();
+       m_Ignore = std::stack<Expression *>();
+       m_FKVar = std::stack<String>();
+       m_FVVar = std::stack<String>();
+       m_FTerm = std::stack<Expression *>();
+       m_Expressions.push(std::vector<Expression *>());
+
        try {
-               yyparse(this);
+               if (yyparse(this) != 0) {
+                       m_Expressions.pop();
+                       return NULL;
+               }
+
+               DictExpression *expr = new DictExpression(m_Expressions.top());
+               m_Expressions.pop();
+               expr->MakeInline();
+               return expr;
+       } catch (const ConfigError& ex) {
+               const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
+               ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
        } catch (const std::exception& ex) {
                ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
        }
+
+       m_Expressions.pop();
+
+       return NULL;
 }
 
 #define scanner (context->GetScanner())
@@ -168,35 +281,16 @@ void ConfigCompiler::Compile(void)
 %}
 
 %%
-statements: /* empty */
-       | statements statement
-       ;
-
-statement: object | type | include | include_recursive | library | variable | apply
+statements: newlines
+       | statement sep
+       | statements statement sep
        ;
 
-include: T_INCLUDE value
+statement: type | library | constant
+       { }
+       | lterm
        {
-               context->HandleInclude(*$2, false, yylloc);
-               delete $2;
-       }
-       | T_INCLUDE T_STRING_ANGLE
-       {
-               context->HandleInclude($2, true, yylloc);
-               free($2);
-       }
-       ;
-
-include_recursive: T_INCLUDE_RECURSIVE value
-       {
-               context->HandleIncludeRecursive(*$2, "*.conf", yylloc);
-               delete $2;
-       }
-       | T_INCLUDE_RECURSIVE value value
-       {
-               context->HandleIncludeRecursive(*$2, *$3, yylloc);
-               delete $2;
-               delete $3;
+               m_Expressions.top().push_back($1);
        }
        ;
 
@@ -207,32 +301,14 @@ library: T_LIBRARY T_STRING
        }
        ;
 
-variable: variable_decl identifier T_SET value
+constant: T_CONST identifier T_SET rterm
        {
-               Value *value = $4;
-               if (value->IsObjectType<ExpressionList>()) {
-                       Dictionary::Ptr dict = make_shared<Dictionary>();
-                       ExpressionList::Ptr exprl = *value;
-                       exprl->Execute(dict);
-                       delete value;
-                       value = new Value(dict);
-               }
-
-               ScriptVariable::Ptr sv = ScriptVariable::Set($2, *value);
-               sv->SetConstant(true);
-
+               VMFrame frame;
+               ScriptVariable::Ptr sv = ScriptVariable::Set($2, $4->Evaluate(frame));
                free($2);
-               delete value;
-       }
-       ;
+               delete $4;
 
-variable_decl: T_VAR
-       {
-               $$ = true;
-       }
-       | T_CONST
-       {
-               $$ = false;
+               sv->SetConstant(true);
        }
        ;
 
@@ -243,48 +319,35 @@ identifier: T_IDENTIFIER
        }
        ;
 
-type: partial_specifier T_TYPE identifier
+type: T_TYPE identifier
        {
-               String name = String($3);
-               free($3);
+               String name = String($2);
+               free($2);
 
                m_Type = ConfigType::GetByName(name);
 
                if (!m_Type) {
-                       if ($1)
-                               BOOST_THROW_EXCEPTION(std::invalid_argument("Partial type definition for unknown type '" + name + "'"));
-
-                       m_Type = make_shared<ConfigType>(name, yylloc);
+                       m_Type = new ConfigType(name, DebugInfoRange(@1, @2));
                        m_Type->Register();
                }
        }
        type_inherits_specifier typerulelist
        {
-               TypeRuleList::Ptr ruleList = *$6;
+               TypeRuleList::Ptr ruleList = *$5;
+               delete $5;
+
                m_Type->GetRuleList()->AddRules(ruleList);
                m_Type->GetRuleList()->AddRequires(ruleList);
 
                String validator = ruleList->GetValidator();
                if (!validator.IsEmpty())
                        m_Type->GetRuleList()->SetValidator(validator);
-
-               delete $6;
-       }
-       ;
-
-partial_specifier: /* Empty */
-       {
-               $$ = 0;
-       }
-       | T_PARTIAL
-       {
-               $$ = 1;
        }
        ;
 
 typerulelist: '{'
        {
-               m_RuleLists.push(make_shared<TypeRuleList>());
+               m_RuleLists.push(new TypeRuleList());
        }
        typerules
        '}'
@@ -295,11 +358,11 @@ typerulelist: '{'
        ;
 
 typerules: typerules_inner
-       | typerules_inner ','
+       | typerules_inner sep
 
 typerules_inner: /* empty */
        | typerule
-       | typerules_inner ',' typerule
+       | typerules_inner sep typerule
        ;
 
 typerule: T_REQUIRE T_STRING
@@ -314,14 +377,14 @@ typerule: T_REQUIRE T_STRING
        }
        | T_ATTRIBUTE type T_STRING
        {
-               TypeRule rule($2, String(), $3, TypeRuleList::Ptr(), yylloc);
+               TypeRule rule($2, String(), $3, TypeRuleList::Ptr(), DebugInfoRange(@1, @3));
                free($3);
 
                m_RuleLists.top()->AddRule(rule);
        }
        | T_ATTRIBUTE T_TYPE_NAME '(' identifier ')' T_STRING
        {
-               TypeRule rule($2, $4, $6, TypeRuleList::Ptr(), yylloc);
+               TypeRule rule($2, $4, $6, TypeRuleList::Ptr(), DebugInfoRange(@1, @6));
                free($4);
                free($6);
 
@@ -329,7 +392,7 @@ typerule: T_REQUIRE T_STRING
        }
        | T_ATTRIBUTE type T_STRING typerulelist
        {
-               TypeRule rule($2, String(), $3, *$4, yylloc);
+               TypeRule rule($2, String(), $3, *$4, DebugInfoRange(@1, @4));
                free($3);
                delete $4;
                m_RuleLists.top()->AddRule(rule);
@@ -358,63 +421,79 @@ type: T_TYPE_DICTIONARY
 
 object:
        {
-               m_Abstract = false;
+               m_Abstract.push(false);
+               m_ObjectAssign.push(true);
+               m_SeenAssign.push(false);
+               m_Assign.push(NULL);
+               m_Ignore.push(NULL);
        }
-object_declaration identifier T_STRING object_inherits_specifier expressionlist
+       object_declaration identifier rterm use_specifier rterm_scope
        {
-               ConfigItemBuilder::Ptr item = make_shared<ConfigItemBuilder>(yylloc);
+               m_ObjectAssign.pop();
 
-               item->SetType($3);
-
-               if (strchr($4, '!') != NULL) {
-                       std::ostringstream msgbuf;
-                       msgbuf << "Name for object '" << $4 << "' of type '" << $3 << "' is invalid: Object names may not contain '!'";
-                       free($3);
-                       BOOST_THROW_EXCEPTION(std::invalid_argument(msgbuf.str()));
-               }
+               bool abstract = m_Abstract.top();
+               m_Abstract.pop();
 
+               String type = $3;
                free($3);
 
-               item->SetName($4);
-               free($4);
+               DictExpression *exprl = dynamic_cast<DictExpression *>($6);
+               exprl->MakeInline();
 
-               if ($5) {
-                       BOOST_FOREACH(const String& parent, *$5) {
-                               item->AddParent(parent);
-                       }
+               bool seen_assign = m_SeenAssign.top();
+               m_SeenAssign.pop();
 
-                       delete $5;
-               }
+               Expression *ignore = m_Ignore.top();
+               m_Ignore.pop();
 
-               if ($6) {
-                       ExpressionList::Ptr exprl = ExpressionList::Ptr($6);
-                       item->AddExpressionList(exprl);
-               }
+               Expression *assign = m_Assign.top();
+               m_Assign.pop();
+
+               Expression *filter = NULL;
+
+               if (seen_assign) {
+                       if (!ObjectRule::IsValidSourceType(type))
+                               BOOST_THROW_EXCEPTION(ConfigError("object rule 'assign' cannot be used for type '" + type + "'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
 
-               item->SetAbstract(m_Abstract);
+                       if (ignore) {
+                               Expression *rex = new LogicalNegateExpression(ignore, DebugInfoRange(@2, @5));
 
-               item->Compile()->Register();
-               item.reset();
+                               filter = new LogicalAndExpression(assign, rex, DebugInfoRange(@2, @5));
+                       } else
+                               filter = assign;
+               }
+
+               $$ = new ObjectExpression(abstract, type, $4, filter, context->GetZone(), $5, exprl, DebugInfoRange(@2, @5));
        }
        ;
 
 object_declaration: T_OBJECT
        | T_TEMPLATE
        {
-               m_Abstract = true;
+               m_Abstract.top() = true;
        }
 
-object_inherits_list:
+identifier_items: identifier_items_inner
        {
-               $$ = NULL;
+               $$ = $1;
        }
-       | T_STRING
+       | identifier_items_inner ','
+       {
+               $$ = $1;
+       }
+       ;
+
+identifier_items_inner: /* empty */
+       {
+               $$ = new std::vector<String>();
+       }
+       | identifier
        {
                $$ = new std::vector<String>();
                $$->push_back($1);
                free($1);
        }
-       | object_inherits_list ',' T_STRING
+       | identifier_items_inner ',' identifier
        {
                if ($1)
                        $$ = $1;
@@ -426,269 +505,516 @@ object_inherits_list:
        }
        ;
 
-object_inherits_specifier:
+indexer: identifier
        {
-               $$ = NULL;
+               $$ = new std::vector<Expression *>();
+               $$->push_back(MakeLiteral($1));
+               free($1);
        }
-       | T_INHERITS object_inherits_list
+       | identifier indexer_items
        {
                $$ = $2;
+               $$->insert($$->begin(), MakeLiteral($1));
+               free($1);
        }
        ;
 
-expressionlist: '{' expressions        '}'
+indexer_items: indexer_item
        {
-               if ($2)
-                       $$ = $2;
-               else
-                       $$ = new ExpressionList();
+               $$ = new std::vector<Expression *>();
+               $$->push_back($1);
+       }
+       | indexer_items indexer_item
+       {
+               $$ = $1;
+               $$->push_back($2);
        }
        ;
 
-expressions: expressions_inner
+indexer_item: '.' identifier
        {
-               $$ = $1;
+               $$ = MakeLiteral($2);
+               free($2);
+       }
+       | '[' rterm ']'
+       {
+               $$ = $2;
        }
-       | expressions_inner ','
+       ;
+
+combined_set_op: T_SET
+       | T_SET_ADD
+       | T_SET_SUBTRACT
+       | T_SET_MULTIPLY
+       | T_SET_DIVIDE
        {
                $$ = $1;
        }
+       ;
 
-expressions_inner: /* empty */
+lterm_items: /* empty */
        {
-               $$ = NULL;
+               $$ = new std::vector<Expression *>();
        }
-       | expression
+       | lterm_items_inner
        {
-               $$ = new ExpressionList();
-               $$->AddExpression(*$1);
-               delete $1;
+               $$ = $1;
        }
-       | expressions_inner ',' expression
+       | lterm_items_inner sep
+       {
+               $$ = $1;
+       }
+
+lterm_items_inner: lterm
+       {
+               $$ = new std::vector<Expression *>();
+               $$->push_back($1);
+       }
+       | lterm_items_inner sep lterm
        {
                if ($1)
                        $$ = $1;
                else
-                       $$ = new ExpressionList();
+                       $$ = new std::vector<Expression *>();
 
-               $$->AddExpression(*$3);
-               delete $3;
+               $$->push_back($3);
        }
        ;
 
-expression: identifier operator value
+lterm: T_LOCAL indexer combined_set_op rterm
        {
-               $$ = new Expression($1, $2, *$3, yylloc);
-               free($1);
-               delete $3;
+               $$ = new SetExpression(*$2, $3, $4, true, DebugInfoRange(@1, @4));
+               delete $2;
        }
-       | identifier '[' T_STRING ']' operator value
+       | indexer combined_set_op rterm
        {
-               Expression subexpr($3, $5, *$6, yylloc);
-               free($3);
-               delete $6;
+               $$ = new SetExpression(*$1, $2, $3, false, DebugInfoRange(@1, @3));
+               delete $1;
+       }
+       | T_INCLUDE rterm
+       {
+               VMFrame frame;
+               $$ = context->HandleInclude($2->Evaluate(frame), false, DebugInfoRange(@1, @2));
+               delete $2;
+       }
+       | T_INCLUDE T_STRING_ANGLE
+       {
+               $$ = context->HandleInclude($2, true, DebugInfoRange(@1, @2));
+               free($2);
+       }
+       | T_INCLUDE_RECURSIVE rterm
+       {
+               VMFrame frame;
+               $$ = context->HandleIncludeRecursive($2->Evaluate(frame), "*.conf", DebugInfoRange(@1, @2));
+               delete $2;
+       }
+       | T_INCLUDE_RECURSIVE rterm ',' rterm
+       {
+               VMFrame frame;
+               $$ = context->HandleIncludeRecursive($2->Evaluate(frame), $4->Evaluate(frame), DebugInfoRange(@1, @4));
+               delete $2;
+               delete $4;
+       }
+       | T_IMPORT rterm
+       {
+               $$ = new ImportExpression($2, DebugInfoRange(@1, @2));
+       }
+       | T_ASSIGN T_WHERE rterm
+       {
+               if ((m_Apply.empty() || !m_Apply.top()) && (m_ObjectAssign.empty() || !m_ObjectAssign.top()))
+                       BOOST_THROW_EXCEPTION(ConfigError("'assign' keyword not valid in this context."));
 
-               ExpressionList::Ptr subexprl = make_shared<ExpressionList>();
-               subexprl->AddExpression(subexpr);
+               m_SeenAssign.top() = true;
 
-               $$ = new Expression($1, OperatorPlus, subexprl, yylloc);
-               free($1);
+               if (m_Assign.top())
+                       m_Assign.top() = new LogicalOrExpression(m_Assign.top(), $3, DebugInfoRange(@1, @3));
+               else
+                       m_Assign.top() = $3;
+
+               $$ = MakeLiteral();
        }
-       ;
+       | T_IGNORE T_WHERE rterm
+       {
+               if ((m_Apply.empty() || !m_Apply.top()) && (m_ObjectAssign.empty() || !m_ObjectAssign.top()))
+                       BOOST_THROW_EXCEPTION(ConfigError("'ignore' keyword not valid in this context."));
+
+               if (m_Ignore.top())
+                       m_Ignore.top() = new LogicalOrExpression(m_Ignore.top(), $3, DebugInfoRange(@1, @3));
+               else
+                       m_Ignore.top() = $3;
 
-operator: T_SET
-       | T_PLUS_EQUAL
-       | T_MINUS_EQUAL
-       | T_MULTIPLY_EQUAL
-       | T_DIVIDE_EQUAL
+               $$ = MakeLiteral();
+       }
+       | T_RETURN rterm
+       {
+               std::vector<Expression *> vname;
+               vname.push_back(MakeLiteral("__result"));
+               $$ = new SetExpression(vname, OpSetLiteral, $2, false, DebugInfoRange(@1, @2));
+       }
+       | apply
+       {
+               $$ = $1;
+       }
+       | object
+       {
+               $$ = $1;
+       }
+       | rterm
        {
                $$ = $1;
        }
        ;
-
-array_items: array_items_inner
+       
+rterm_items: /* empty */
+       {
+               $$ = new std::vector<Expression *>();
+       }
+       | rterm_items_inner
        {
                $$ = $1;
        }
-       | array_items_inner ','
+       | rterm_items_inner arraysep
        {
                $$ = $1;
        }
+       ;
 
-array_items_inner: /* empty */
+rterm_items_inner: rterm
        {
-               $$ = NULL;
+               $$ = new std::vector<Expression *>();
+               $$->push_back($1);
        }
-       | aexpression
+       | rterm_items_inner arraysep rterm
        {
-               $$ = new Array();
-               $$->Add(*$1);
-               delete $1;
+               $$ = $1;
+               $$->push_back($3);
        }
-       | array_items_inner ',' aexpression
+       ;
+
+rterm_array: '[' newlines rterm_items newlines ']'
        {
-               if ($1)
-                       $$ = $1;
-               else
-                       $$ = new Array();
+               $$ = new ArrayExpression(*$3, DebugInfoRange(@1, @5));
+               delete $3;
+       }
+       | '[' newlines rterm_items ']'
+       {
+               $$ = new ArrayExpression(*$3, DebugInfoRange(@1, @4));
+               delete $3;
+       }
+       | '[' rterm_items newlines ']'
+       {
+               $$ = new ArrayExpression(*$2, DebugInfoRange(@1, @4));
+               delete $2;
+       }
+       | '[' rterm_items ']'
+       {
+               $$ = new ArrayExpression(*$2, DebugInfoRange(@1, @3));
+               delete $2;
+       }
+       ;
 
-               $$->Add(*$3);
+rterm_scope: '{' newlines lterm_items newlines '}'
+       {
+               $$ = new DictExpression(*$3, DebugInfoRange(@1, @5));
+               delete $3;
+       }
+       | '{' newlines lterm_items '}'
+       {
+               $$ = new DictExpression(*$3, DebugInfoRange(@1, @4));
                delete $3;
        }
+       | '{' lterm_items newlines '}'
+       {
+               $$ = new DictExpression(*$2, DebugInfoRange(@1, @4));
+               delete $2;
+       }
+       | '{' lterm_items '}'
+       {
+               $$ = new DictExpression(*$2, DebugInfoRange(@1, @3));
+               delete $2;
+       }
        ;
 
-aexpression: T_STRING
+rterm: T_STRING
        {
-               $$ = new Value(make_shared<AExpression>(AEReturn, AValue(ATSimple, $1), yylloc));
+               $$ = MakeLiteral($1);
                free($1);
        }
        | T_NUMBER
        {
-               $$ = new Value(make_shared<AExpression>(AEReturn, AValue(ATSimple, $1), yylloc));
+               $$ = MakeLiteral($1);
        }
        | T_NULL
        {
-               $$ = new Value(make_shared<AExpression>(AEReturn, AValue(ATSimple, Empty), yylloc));
+               $$ = MakeLiteral();
        }
-       | T_IDENTIFIER '(' array_items ')'
+       | rterm '.' T_IDENTIFIER
        {
-               Array::Ptr arguments = Array::Ptr($3);
-               $$ = new Value(make_shared<AExpression>(AEFunctionCall, AValue(ATSimple, $1), AValue(ATSimple, arguments), yylloc));
-               free($1);
+               $$ = new IndexerExpression($1, MakeLiteral($3), DebugInfoRange(@1, @3));
+               free($3);
+       }
+       | rterm '(' rterm_items ')'
+       {
+               $$ = new FunctionCallExpression($1, *$3, DebugInfoRange(@1, @4));
+               delete $3;
        }
        | T_IDENTIFIER
        {
-               $$ = new Value(make_shared<AExpression>(AEReturn, AValue(ATVariable, $1), yylloc));
+               $$ = new VariableExpression($1, @1);
                free($1);
        }
-       | '!' aexpression
+       | '!' rterm
        {
-               $$ = new Value(make_shared<AExpression>(AENegate, static_cast<AExpression::Ptr>(*$2), yylloc));
-               delete $2;
+               $$ = new LogicalNegateExpression($2, DebugInfoRange(@1, @2));
        }
-       | '~' aexpression
+       | '~' rterm
        {
-               $$ = new Value(make_shared<AExpression>(AENegate, static_cast<AExpression::Ptr>(*$2), yylloc));
-               delete $2;
+               $$ = new NegateExpression($2, DebugInfoRange(@1, @2));
        }
-       | '[' array_items ']'
+       | rterm '[' rterm ']'
        {
-               $$ = new Value(make_shared<AExpression>(AEArray, AValue(ATSimple, Array::Ptr($2)), yylloc));
+               $$ = new IndexerExpression($1, $3, DebugInfoRange(@1, @4));
        }
-       | '(' aexpression ')'
+       | rterm_array
        {
-               $$ = $2;
+               $$ = $1;
        }
-       | aexpression '+' aexpression
+       | rterm_scope
        {
-               $$ = new Value(make_shared<AExpression>(AEAdd, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = $1;
        }
-       | aexpression '-' aexpression
-       {
-               $$ = new Value(make_shared<AExpression>(AESubtract, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+       | '('
+       {
+               ignore_newlines++;
+       }
+       rterm ')'
+       {
+               ignore_newlines--;
+               $$ = $3;
+       }
+       | rterm T_LOGICAL_OR rterm { MakeRBinaryOp<LogicalOrExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_LOGICAL_AND rterm { MakeRBinaryOp<LogicalAndExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_BINARY_OR rterm { MakeRBinaryOp<BinaryOrExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_BINARY_AND rterm { MakeRBinaryOp<BinaryAndExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_IN rterm { MakeRBinaryOp<InExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_NOT_IN rterm { MakeRBinaryOp<NotInExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_EQUAL rterm { MakeRBinaryOp<EqualExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_NOT_EQUAL rterm { MakeRBinaryOp<NotEqualExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_LESS_THAN rterm { MakeRBinaryOp<LessThanExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_LESS_THAN_OR_EQUAL rterm { MakeRBinaryOp<LessThanOrEqualExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_GREATER_THAN rterm { MakeRBinaryOp<GreaterThanExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_GREATER_THAN_OR_EQUAL rterm { MakeRBinaryOp<GreaterThanOrEqualExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_SHIFT_LEFT rterm { MakeRBinaryOp<ShiftLeftExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_SHIFT_RIGHT rterm { MakeRBinaryOp<ShiftRightExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_PLUS rterm { MakeRBinaryOp<AddExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_MINUS rterm { MakeRBinaryOp<SubtractExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_MULTIPLY rterm { MakeRBinaryOp<MultiplyExpression>(&$$, $1, $3, @1, @3); }
+       | rterm T_DIVIDE_OP rterm { MakeRBinaryOp<DivideExpression>(&$$, $1, $3, @1, @3); }
+       | T_FUNCTION identifier '(' identifier_items ')' use_specifier rterm_scope
+       {
+               DictExpression *aexpr = dynamic_cast<DictExpression *>($7);
+               aexpr->MakeInline();
+
+               $$ = new FunctionExpression($2, *$4, $6, aexpr, DebugInfoRange(@1, @6));
+               free($2);
+               delete $4;
        }
-       | aexpression '*' aexpression
+       | T_FUNCTION '(' identifier_items ')' use_specifier rterm_scope
        {
-               $$ = new Value(make_shared<AExpression>(AEMultiply, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
+               DictExpression *aexpr = dynamic_cast<DictExpression *>($6);
+               aexpr->MakeInline();
+
+               $$ = new FunctionExpression("", *$3, $5, aexpr, DebugInfoRange(@1, @5));
                delete $3;
        }
-       | aexpression '/' aexpression
+       | T_SIGNAL identifier T_SET_ADD rterm
        {
-               $$ = new Value(make_shared<AExpression>(AEDivide, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = new SlotExpression($2, $4, DebugInfoRange(@1, @4));
+               free($2);
        }
-       | aexpression '&' aexpression
+       | T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' rterm_scope
        {
-               $$ = new Value(make_shared<AExpression>(AEBinaryAnd, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = new ForExpression($3, $5, $7, $9, DebugInfoRange(@1, @9));
+               free($3);
+               free($5);
        }
-       | aexpression '|' aexpression
+       | T_FOR '(' identifier T_IN rterm ')' rterm_scope
        {
-               $$ = new Value(make_shared<AExpression>(AEBinaryOr, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = new ForExpression($3, "", $5, $7, DebugInfoRange(@1, @7));
+               free($3);
        }
-       | aexpression T_IN aexpression
+       ;
+
+target_type_specifier: /* empty */
        {
-               $$ = new Value(make_shared<AExpression>(AEIn, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = strdup("");
        }
-       | aexpression T_NOT_IN aexpression
+       | T_TO identifier
        {
-               $$ = new Value(make_shared<AExpression>(AENotIn, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = $2;
        }
-       | aexpression T_EQUAL aexpression
+       ;
+
+use_specifier: /* empty */
        {
-               $$ = new Value(make_shared<AExpression>(AEEqual, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = NULL;
        }
-       | aexpression T_NOT_EQUAL aexpression
+       | T_USE '(' use_specifier_items ')'
        {
-               $$ = new Value(make_shared<AExpression>(AENotEqual, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = $3;
        }
-       | aexpression T_SHIFT_LEFT aexpression
+       ;
+
+use_specifier_items: use_specifier_item
        {
-               $$ = new Value(make_shared<AExpression>(AEShiftLeft, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
+               $$ = new std::map<String, Expression *>();
+               $$->insert(*$1);
                delete $1;
-               delete $3;
        }
-       | aexpression T_SHIFT_RIGHT aexpression
+       | use_specifier_items ',' use_specifier_item
        {
-               $$ = new Value(make_shared<AExpression>(AEShiftRight, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
+               $$ = $1;
+               $$->insert(*$3);
                delete $3;
        }
-       | aexpression T_LOGICAL_AND aexpression
+       ;
+
+use_specifier_item: identifier
        {
-               $$ = new Value(make_shared<AExpression>(AELogicalAnd, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = new std::pair<String, Expression *>($1, new VariableExpression($1, @1));
        }
-       | aexpression T_LOGICAL_OR aexpression
+       | identifier T_SET rterm
        {
-               $$ = new Value(make_shared<AExpression>(AELogicalOr, static_cast<AExpression::Ptr>(*$1), static_cast<AExpression::Ptr>(*$3), yylloc));
-               delete $1;
-               delete $3;
+               $$ = new std::pair<String, Expression *>($1, $3);
        }
        ;
 
-value: expressionlist
+apply_for_specifier: /* empty */
+       | T_APPLY_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')'
        {
-               ExpressionList::Ptr exprl = ExpressionList::Ptr($1);
-               $$ = new Value(exprl);
+               m_FKVar.top() = $3;
+               free($3);
+
+               m_FVVar.top() = $5;
+               free($5);
+
+               m_FTerm.top() = $7;
        }
-       | aexpression
+       | T_APPLY_FOR '(' identifier T_IN rterm ')'
        {
-               AExpression::Ptr aexpr = *$1;
-               $$ = new Value(aexpr->Evaluate(Dictionary::Ptr()));
-               delete $1;
+               m_FKVar.top() = $3;
+               free($3);
+
+               m_FVVar.top() = "";
+
+               m_FTerm.top() = $5;
        }
        ;
 
-optional_template: /* empty */
-       | T_TEMPLATE
+optional_rterm: /* empty */
+       {
+               $$ = MakeLiteral();
+       }
+       | rterm
+       {
+               $$ = $1;
+       }
        ;
 
-apply: T_APPLY optional_template identifier identifier T_TO identifier T_WHERE aexpression
+apply:
        {
-               if (!ApplyRule::IsValidCombination($3, $6)) {
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("'apply' cannot be used with types '" + String($3) + "' and '" + String($6) + "'."));
+               m_Apply.push(true);
+               m_SeenAssign.push(false);
+               m_Assign.push(NULL);
+               m_Ignore.push(NULL);
+               m_FKVar.push("");
+               m_FVVar.push("");
+               m_FTerm.push(NULL);
+       }
+       T_APPLY identifier optional_rterm apply_for_specifier target_type_specifier use_specifier rterm_scope
+       {
+               m_Apply.pop();
+
+               String type = $3;
+               free($3);
+               String target = $6;
+               free($6);
+
+               if (!ApplyRule::IsValidSourceType(type))
+                       BOOST_THROW_EXCEPTION(ConfigError("'apply' cannot be used with type '" + type + "'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
+
+               if (!ApplyRule::IsValidTargetType(type, target)) {
+                       if (target == "") {
+                               std::vector<String> types = ApplyRule::GetTargetTypes(type);
+                               String typeNames;
+
+                               for (std::vector<String>::size_type i = 0; i < types.size(); i++) {
+                                       if (typeNames != "") {
+                                               if (i == types.size() - 1)
+                                                       typeNames += " or ";
+                                               else
+                                                       typeNames += ", ";
+                                       }
+
+                                       typeNames += "'" + types[i] + "'";
+                               }
+
+                               BOOST_THROW_EXCEPTION(ConfigError("'apply' target type is ambiguous (can be one of " + typeNames + "): use 'to' to specify a type") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
+                       } else
+                               BOOST_THROW_EXCEPTION(ConfigError("'apply' target type '" + target + "' is invalid") << errinfo_debuginfo(DebugInfoRange(@2, @5)));
                }
 
-               ApplyRule::AddRule($3, $4, $6, *$8, yylloc);
-               delete $8;
+               DictExpression *exprl = dynamic_cast<DictExpression *>($8);
+               exprl->MakeInline();
+
+               // assign && !ignore
+               if (!m_SeenAssign.top())
+                       BOOST_THROW_EXCEPTION(ConfigError("'apply' is missing 'assign'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
+
+               m_SeenAssign.pop();
+
+               Expression *ignore = m_Ignore.top();
+               m_Ignore.pop();
+
+               Expression *assign = m_Assign.top();
+               m_Assign.pop();
+
+               Expression *filter;
+
+               if (ignore) {
+                       Expression *rex = new LogicalNegateExpression(ignore, DebugInfoRange(@2, @5));
+
+                       filter = new LogicalAndExpression(assign, rex, DebugInfoRange(@2, @5));
+               } else
+                       filter = assign;
+
+               String fkvar = m_FKVar.top();
+               m_FKVar.pop();
+
+               String fvvar = m_FVVar.top();
+               m_FVVar.pop();
+
+               Expression *fterm = m_FTerm.top();
+               m_FTerm.pop();
+
+               $$ = new ApplyExpression(type, target, $4, filter, fkvar, fvvar, fterm, $7, exprl, DebugInfoRange(@2, @7));
        }
+       ;
+
+newlines: T_NEWLINE
+       | T_NEWLINE newlines
+       ;
+
+/* required separator */
+sep: ',' newlines
+       | ','
+       | ';' newlines
+       | ';'
+       | newlines
+       ;
+
+arraysep: ',' newlines
+       | ','
+       ;
+
 %%