]> granicus.if.org Git - icinga2/blob - lib/config/config_parser.yy
Implement support for the "package" keyword.
[icinga2] / lib / config / config_parser.yy
1 %{
2  #define YYDEBUG 1
3  
4 /******************************************************************************
5  * Icinga 2                                                                   *
6  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
7  *                                                                            *
8  * This program is free software; you can redistribute it and/or              *
9  * modify it under the terms of the GNU General Public License                *
10  * as published by the Free Software Foundation; either version 2             *
11  * of the License, or (at your option) any later version.                     *
12  *                                                                            *
13  * This program is distributed in the hope that it will be useful,            *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
16  * GNU General Public License for more details.                               *
17  *                                                                            *
18  * You should have received a copy of the GNU General Public License          *
19  * along with this program; if not, write to the Free Software Foundation     *
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
21  ******************************************************************************/
22
23 #include "i2-config.h"
24 #include "config/configitembuilder.h"
25 #include "config/configcompiler.h"
26 #include "config/configcompilercontext.h"
27 #include "config/configerror.h"
28 #include "config/typerule.h"
29 #include "config/typerulelist.h"
30 #include "config/aexpression.h"
31 #include "config/applyrule.h"
32 #include "config/objectrule.h"
33 #include "base/value.h"
34 #include "base/utility.h"
35 #include "base/array.h"
36 #include "base/scriptvariable.h"
37 #include "base/exception.h"
38 #include "base/dynamictype.h"
39 #include <sstream>
40 #include <stack>
41 #include <boost/foreach.hpp>
42
43 #define YYLTYPE icinga::DebugInfo
44 #define YYERROR_VERBOSE
45
46 #define YYLLOC_DEFAULT(Current, Rhs, N)                                 \
47 do {                                                                    \
48         if (N) {                                                        \
49                 (Current).Path = YYRHSLOC(Rhs, 1).Path;                 \
50                 (Current).FirstLine = YYRHSLOC(Rhs, 1).FirstLine;       \
51                 (Current).FirstColumn = YYRHSLOC(Rhs, 1).FirstColumn;   \
52                 (Current).LastLine = YYRHSLOC(Rhs, N).LastLine;         \
53                 (Current).LastColumn = YYRHSLOC(Rhs, N).LastColumn;     \
54         } else {                                                        \
55                 (Current).Path = YYRHSLOC(Rhs, 0).Path;                 \
56                 (Current).FirstLine = (Current).LastLine =              \
57                 YYRHSLOC(Rhs, 0).LastLine;                              \
58                 (Current).FirstColumn = (Current).LastColumn =          \
59                 YYRHSLOC(Rhs, 0).LastColumn;                            \
60         }                                                               \
61 } while (0)
62
63 #define YY_LOCATION_PRINT(file, loc)                    \
64 do {                                                    \
65        std::ostringstream msgbuf;                       \
66        msgbuf << loc;                                   \
67        std::string str = msgbuf.str();                  \
68        fputs(str.c_str(), file);                        \
69 } while (0)
70
71 using namespace icinga;
72
73 int ignore_newlines = 0;
74
75 static void MakeRBinaryOp(Value** result, AExpression::OpCallback& op, Value *left, Value *right, DebugInfo& diLeft, DebugInfo& diRight)
76 {
77         *result = new Value(make_shared<AExpression>(op, *left, *right, DebugInfoRange(diLeft, diRight)));
78         delete left;
79         delete right;
80 }
81
82 %}
83
84 %pure-parser
85
86 %locations
87 %defines
88 %error-verbose
89
90 %parse-param { ConfigCompiler *context }
91 %lex-param { void *scanner }
92
93 %union {
94         char *text;
95         double num;
96         icinga::Value *variant;
97         icinga::AExpression::OpCallback op;
98         icinga::TypeSpecifier type;
99         std::vector<String> *slist;
100         Array *array;
101 }
102
103 %token T_NEWLINE "new-line"
104 %token <text> T_STRING
105 %token <text> T_STRING_ANGLE
106 %token <num> T_NUMBER
107 %token T_NULL
108 %token <text> T_IDENTIFIER
109
110 %token <op> T_SET "= (T_SET)"
111 %token <op> T_SET_PLUS "+= (T_SET_PLUS)"
112 %token <op> T_SET_MINUS "-= (T_SET_MINUS)"
113 %token <op> T_SET_MULTIPLY "*= (T_SET_MULTIPLY)"
114 %token <op> T_SET_DIVIDE "/= (T_SET_DIVIDE)"
115
116 %token <op> T_SHIFT_LEFT "<< (T_SHIFT_LEFT)"
117 %token <op> T_SHIFT_RIGHT ">> (T_SHIFT_RIGHT)"
118 %token <op> T_EQUAL "== (T_EQUAL)"
119 %token <op> T_NOT_EQUAL "!= (T_NOT_EQUAL)"
120 %token <op> T_IN "in (T_IN)"
121 %token <op> T_NOT_IN "!in (T_NOT_IN)"
122 %token <op> T_LOGICAL_AND "&& (T_LOGICAL_AND)"
123 %token <op> T_LOGICAL_OR "|| (T_LOGICAL_OR)"
124 %token <op> T_LESS_THAN_OR_EQUAL "<= (T_LESS_THAN_OR_EQUAL)"
125 %token <op> T_GREATER_THAN_OR_EQUAL ">= (T_GREATER_THAN_OR_EQUAL)"
126 %token <op> T_PLUS "+ (T_PLUS)"
127 %token <op> T_MINUS "- (T_MINUS)"
128 %token <op> T_MULTIPLY "* (T_MULTIPLY)"
129 %token <op> T_DIVIDE_OP "/ (T_DIVIDE_OP)"
130 %token <op> T_BINARY_AND "& (T_BINARY_AND)"
131 %token <op> T_BINARY_OR "| (T_BINARY_OR)"
132 %token <op> T_LESS_THAN "< (T_LESS_THAN)"
133 %token <op> T_GREATER_THAN "> (T_GREATER_THAN)"
134
135 %token T_CONST "const (T_CONST)"
136 %token <type> T_TYPE_DICTIONARY "dictionary (T_TYPE_DICTIONARY)"
137 %token <type> T_TYPE_ARRAY "array (T_TYPE_ARRAY)"
138 %token <type> T_TYPE_NUMBER "number (T_TYPE_NUMBER)"
139 %token <type> T_TYPE_STRING "string (T_TYPE_STRING)"
140 %token <type> T_TYPE_SCALAR "scalar (T_TYPE_SCALAR)"
141 %token <type> T_TYPE_ANY "any (T_TYPE_ANY)"
142 %token <type> T_TYPE_NAME "name (T_TYPE_NAME)"
143 %token T_VALIDATOR "%validator (T_VALIDATOR)"
144 %token T_REQUIRE "%require (T_REQUIRE)"
145 %token T_ATTRIBUTE "%attribute (T_ATTRIBUTE)"
146 %token T_TYPE "type (T_TYPE)"
147 %token T_OBJECT "object (T_OBJECT)"
148 %token T_TEMPLATE "template (T_TEMPLATE)"
149 %token T_INCLUDE "include (T_INCLUDE)"
150 %token T_INCLUDE_RECURSIVE "include_recursive (T_INCLUDE_RECURSIVE)"
151 %token T_LIBRARY "library (T_LIBRARY)"
152 %token T_INHERITS "inherits (T_INHERITS)"
153 %token T_PARTIAL "partial (T_PARTIAL)"
154 %token T_APPLY "apply (T_APPLY)"
155 %token T_TO "to (T_TO)"
156 %token T_WHERE "where (T_WHERE)"
157 %token T_IMPORT "import (T_IMPORT)"
158 %token T_ASSIGN "assign (T_ASSIGN)"
159 %token T_IGNORE "ignore (T_IGNORE)"
160 %token T_FUNCTION "function (T_FUNCTION)"
161 %token T_LAMBDA "lambda (T_LAMBDA)"
162 %token T_RETURN "return (T_RETURN)"
163 %token T_PACKAGE "package (T_PACKAGE)"
164
165 %type <text> identifier
166 %type <array> rterm_items
167 %type <array> rterm_items_inner
168 %type <array> identifier_items
169 %type <array> identifier_items_inner
170 %type <array> lterm_items
171 %type <array> lterm_items_inner
172 %type <variant> typerulelist
173 %type <op> lbinary_op
174 %type <type> type
175 %type <num> partial_specifier
176 %type <variant> rterm
177 %type <variant> rterm_array
178 %type <variant> rterm_scope
179 %type <variant> lterm
180 %type <variant> object
181 %type <variant> apply
182 %type <text> target_type_specifier
183
184 %left T_LOGICAL_OR
185 %left T_LOGICAL_AND
186 %left T_BINARY_OR
187 %left T_BINARY_AND
188 %left T_IN
189 %left T_NOT_IN
190 %left T_EQUAL T_NOT_EQUAL
191 %left T_LESS_THAN T_LESS_THAN_OR_EQUAL T_GREATER_THAN T_GREATER_THAN_OR_EQUAL
192 %left T_SHIFT_LEFT T_SHIFT_RIGHT
193 %left T_PLUS T_MINUS
194 %left T_MULTIPLY T_DIVIDE_OP
195 %right '!' '~'
196 %left '.' '(' '['
197 %right ':'
198 %{
199
200 int yylex(YYSTYPE *lvalp, YYLTYPE *llocp, void *scanner);
201
202 void yyerror(YYLTYPE *locp, ConfigCompiler *, const char *err)
203 {
204         std::ostringstream message;
205         message << *locp << ": " << err;
206         ConfigCompilerContext::GetInstance()->AddMessage(true, message.str(), *locp);
207 }
208
209 int yyparse(ConfigCompiler *context);
210
211 static bool m_Abstract;
212
213 static std::stack<TypeRuleList::Ptr> m_RuleLists;
214 static ConfigType::Ptr m_Type;
215
216 static Dictionary::Ptr m_ModuleScope;
217 static String m_Package;
218 static int m_StatementNum;
219
220 static bool m_Apply;
221 static bool m_ObjectAssign;
222 static bool m_SeenAssign;
223 static AExpression::Ptr m_Assign;
224 static AExpression::Ptr m_Ignore;
225
226 void ConfigCompiler::Compile(void)
227 {
228         m_ModuleScope = make_shared<Dictionary>();
229         
230         String parentPackage = m_Package;
231         int parentStatementNum = m_StatementNum;
232         m_StatementNum = 0;
233
234         try {
235                 yyparse(this);
236         } catch (const ConfigError& ex) {
237                 const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
238                 ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
239         } catch (const std::exception& ex) {
240                 ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
241         }
242
243         m_Package = parentPackage;
244         m_StatementNum = parentStatementNum;
245 }
246
247 #define scanner (context->GetScanner())
248
249 %}
250
251 %%
252 statements: /* empty */
253         | statements statement
254         ;
255
256 statement: type | package | include | include_recursive | library | constant
257         {
258                 m_StatementNum++;
259         }
260         | newlines
261         { }
262         | lterm
263         {
264                 AExpression::Ptr aexpr = *$1;
265                 aexpr->Evaluate(m_ModuleScope);
266                 delete $1;
267
268                 m_StatementNum++;
269         }
270         ;
271
272 package: T_PACKAGE rterm sep
273         {
274                 AExpression::Ptr aexpr = *$2;
275                 delete $2;
276
277                 if (!m_Package.IsEmpty())
278                         BOOST_THROW_EXCEPTION(std::invalid_argument("Package name cannot be changed once it's been set."));
279
280                 if (m_StatementNum != 0)
281                         BOOST_THROW_EXCEPTION(std::invalid_argument("'package' directive must be the first statement in a file."));
282
283                 m_Package = aexpr->Evaluate(m_ModuleScope);
284         }
285         | T_PACKAGE rterm rterm_scope sep
286         {
287                 AExpression::Ptr aexpr = *$2;
288                 delete $2;
289
290                 AExpression::Ptr ascope = *$3;
291                 delete $3;
292
293                 if (!m_Package.IsEmpty())
294                         BOOST_THROW_EXCEPTION(std::invalid_argument("Package name cannot be changed once it's been set."));
295
296                 m_Package = aexpr->Evaluate(m_ModuleScope);
297
298                 try {
299                         ascope->Evaluate(m_ModuleScope);
300                         m_Package = String();
301                 } catch (...) {
302                         m_Package = String();
303                 }
304         }
305         ;
306
307 include: T_INCLUDE rterm sep
308         {
309                 AExpression::Ptr aexpr = *$2;
310                 delete $2;
311
312                 context->HandleInclude(aexpr->Evaluate(m_ModuleScope), false, DebugInfoRange(@1, @2));
313         }
314         | T_INCLUDE T_STRING_ANGLE
315         {
316                 context->HandleInclude($2, true, DebugInfoRange(@1, @2));
317                 free($2);
318         }
319         ;
320
321 include_recursive: T_INCLUDE_RECURSIVE rterm
322         {
323                 AExpression::Ptr aexpr = *$2;
324                 delete $2;
325
326                 context->HandleIncludeRecursive(aexpr->Evaluate(m_ModuleScope), "*.conf", DebugInfoRange(@1, @2));
327         }
328         | T_INCLUDE_RECURSIVE rterm ',' rterm
329         {
330                 AExpression::Ptr aexpr1 = *$2;
331                 delete $2;
332
333                 AExpression::Ptr aexpr2 = *$4;
334                 delete $4;
335
336                 context->HandleIncludeRecursive(aexpr1->Evaluate(m_ModuleScope), aexpr2->Evaluate(m_ModuleScope), DebugInfoRange(@1, @4));
337         }
338         ;
339
340 library: T_LIBRARY T_STRING sep
341         {
342                 context->HandleLibrary($2);
343                 free($2);
344         }
345         ;
346
347 constant: T_CONST identifier T_SET rterm sep
348         {
349                 AExpression::Ptr aexpr = *$4;
350                 delete $4;
351
352                 ScriptVariable::Ptr sv = ScriptVariable::Set($2, aexpr->Evaluate(m_ModuleScope));
353                 sv->SetConstant(true);
354
355                 free($2);
356         }
357         ;
358
359 identifier: T_IDENTIFIER
360         | T_STRING
361         {
362                 $$ = $1;
363         }
364         ;
365
366 type: partial_specifier T_TYPE identifier
367         {
368                 String name = String($3);
369                 free($3);
370
371                 m_Type = ConfigType::GetByName(name);
372
373                 if (!m_Type) {
374                         if ($1)
375                                 BOOST_THROW_EXCEPTION(ConfigError("Partial type definition for unknown type '" + name + "'") << errinfo_debuginfo(DebugInfoRange(@1, @3)));
376
377                         m_Type = make_shared<ConfigType>(name, DebugInfoRange(@1, @3));
378                         m_Type->Register();
379                 }
380         }
381         type_inherits_specifier typerulelist sep
382         {
383                 TypeRuleList::Ptr ruleList = *$6;
384                 m_Type->GetRuleList()->AddRules(ruleList);
385                 m_Type->GetRuleList()->AddRequires(ruleList);
386
387                 String validator = ruleList->GetValidator();
388                 if (!validator.IsEmpty())
389                         m_Type->GetRuleList()->SetValidator(validator);
390
391                 delete $6;
392         }
393         ;
394
395 partial_specifier: /* Empty */
396         {
397                 $$ = 0;
398         }
399         | T_PARTIAL
400         {
401                 $$ = 1;
402         }
403         ;
404
405 typerulelist: '{'
406         {
407                 m_RuleLists.push(make_shared<TypeRuleList>());
408         }
409         typerules
410         '}'
411         {
412                 $$ = new Value(m_RuleLists.top());
413                 m_RuleLists.pop();
414         }
415         ;
416
417 typerules: typerules_inner
418         | typerules_inner sep
419
420 typerules_inner: /* empty */
421         | typerule
422         | typerules_inner sep typerule
423         ;
424
425 typerule: T_REQUIRE T_STRING
426         {
427                 m_RuleLists.top()->AddRequire($2);
428                 free($2);
429         }
430         | T_VALIDATOR T_STRING
431         {
432                 m_RuleLists.top()->SetValidator($2);
433                 free($2);
434         }
435         | T_ATTRIBUTE type T_STRING
436         {
437                 TypeRule rule($2, String(), $3, TypeRuleList::Ptr(), DebugInfoRange(@1, @3));
438                 free($3);
439
440                 m_RuleLists.top()->AddRule(rule);
441         }
442         | T_ATTRIBUTE T_TYPE_NAME '(' identifier ')' T_STRING
443         {
444                 TypeRule rule($2, $4, $6, TypeRuleList::Ptr(), DebugInfoRange(@1, @6));
445                 free($4);
446                 free($6);
447
448                 m_RuleLists.top()->AddRule(rule);
449         }
450         | T_ATTRIBUTE type T_STRING typerulelist
451         {
452                 TypeRule rule($2, String(), $3, *$4, DebugInfoRange(@1, @4));
453                 free($3);
454                 delete $4;
455                 m_RuleLists.top()->AddRule(rule);
456         }
457         ;
458
459 type_inherits_specifier: /* empty */
460         | T_INHERITS identifier
461         {
462                 m_Type->SetParent($2);
463                 free($2);
464         }
465         ;
466
467 type: T_TYPE_DICTIONARY
468         | T_TYPE_ARRAY
469         | T_TYPE_NUMBER
470         | T_TYPE_STRING
471         | T_TYPE_SCALAR
472         | T_TYPE_ANY
473         | T_TYPE_NAME
474         {
475                 $$ = $1;
476         }
477         ;
478
479 object:
480         {
481                 m_Abstract = false;
482                 m_ObjectAssign = true;
483                 m_SeenAssign = false;
484                 m_Assign = make_shared<AExpression>(&AExpression::OpLiteral, false, DebugInfo());
485                 m_Ignore = make_shared<AExpression>(&AExpression::OpLiteral, false, DebugInfo());
486         }
487         object_declaration identifier rterm rterm_scope
488         {
489                 m_ObjectAssign = false;
490
491                 Array::Ptr args = make_shared<Array>();
492                 
493                 args->Add(m_Abstract);
494
495                 String type = $3;
496                 args->Add(type);
497                 free($3);
498
499                 args->Add(*$4);
500                 delete $4;
501
502                 AExpression::Ptr exprl = *$5;
503                 delete $5;
504                 exprl->MakeInline();
505
506                 if (m_SeenAssign && !ObjectRule::IsValidSourceType(type))
507                         BOOST_THROW_EXCEPTION(ConfigError("object rule 'assign' cannot be used for type '" + type + "'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
508
509                 AExpression::Ptr rex = make_shared<AExpression>(&AExpression::OpLogicalNegate, m_Ignore, DebugInfoRange(@2, @5));
510                 AExpression::Ptr filter = make_shared<AExpression>(&AExpression::OpLogicalAnd, m_Assign, rex, DebugInfoRange(@2, @5));
511
512                 args->Add(filter);
513
514                 args->Add(m_Package);
515
516                 $$ = new Value(make_shared<AExpression>(&AExpression::OpObject, args, exprl, DebugInfoRange(@2, @5)));
517
518                 m_Assign.reset();
519                 m_Ignore.reset();
520         }
521         ;
522
523 object_declaration: T_OBJECT
524         | T_TEMPLATE
525         {
526                 m_Abstract = true;
527         }
528
529 identifier_items: identifier_items_inner
530         {
531                 $$ = $1;
532         }
533         | identifier_items_inner ','
534         {
535                 $$ = $1;
536         }
537         ;
538
539 identifier_items_inner: /* empty */
540         {
541                 $$ = new Array();
542         }
543         | identifier
544         {
545                 $$ = new Array();
546                 $$->Add($1);
547                 free($1);
548         }
549         | identifier_items_inner ',' identifier
550         {
551                 if ($1)
552                         $$ = $1;
553                 else
554                         $$ = new Array();
555
556                 $$->Add($3);
557                 free($3);
558         }
559         ;
560
561 lbinary_op: T_SET
562         | T_SET_PLUS
563         | T_SET_MINUS
564         | T_SET_MULTIPLY
565         | T_SET_DIVIDE
566         {
567                 $$ = $1;
568         }
569         ;
570
571 lterm_items: /* empty */
572         {
573                 $$ = new Array();
574         }
575         | lterm_items_inner
576         {
577                 $$ = $1;
578         }
579         | lterm_items_inner sep
580         {
581                 $$ = $1;
582         }
583
584 lterm_items_inner: lterm
585         {
586                 $$ = new Array();
587                 $$->Add(*$1);
588                 delete $1;
589         }
590         | lterm_items_inner sep lterm
591         {
592                 if ($1)
593                         $$ = $1;
594                 else
595                         $$ = new Array();
596
597                 $$->Add(*$3);
598                 delete $3;
599         }
600         ;
601
602 lterm: identifier lbinary_op rterm
603         {
604                 AExpression::Ptr aindex = make_shared<AExpression>(&AExpression::OpLiteral, $1, @1);
605                 free($1);
606
607                 $$ = new Value(make_shared<AExpression>($2, aindex, *$3, DebugInfoRange(@1, @3)));
608                 delete $3;
609         }
610         | identifier '[' rterm ']' lbinary_op rterm
611         {
612                 AExpression::Ptr subexpr = make_shared<AExpression>($5, *$3, *$6, DebugInfoRange(@1, @6));
613                 delete $3;
614                 delete $6;
615
616                 Array::Ptr subexprl = make_shared<Array>();
617                 subexprl->Add(subexpr);
618                 
619                 AExpression::Ptr aindex = make_shared<AExpression>(&AExpression::OpLiteral, $1, @1);
620                 free($1);
621
622                 AExpression::Ptr expr = make_shared<AExpression>(&AExpression::OpDict, subexprl, DebugInfoRange(@1, @6));
623                 $$ = new Value(make_shared<AExpression>(&AExpression::OpSetPlus, aindex, expr, DebugInfoRange(@1, @6)));
624         }
625         | identifier '.' T_IDENTIFIER lbinary_op rterm
626         {
627                 AExpression::Ptr aindex = make_shared<AExpression>(&AExpression::OpLiteral, $3, @3);
628                 AExpression::Ptr subexpr = make_shared<AExpression>($4, aindex, *$5, DebugInfoRange(@1, @5));
629                 free($3);
630                 delete $5;
631
632                 Array::Ptr subexprl = make_shared<Array>();
633                 subexprl->Add(subexpr);
634
635                 AExpression::Ptr aindexl = make_shared<AExpression>(&AExpression::OpLiteral, $1, @1);
636                 free($1);
637
638                 AExpression::Ptr expr = make_shared<AExpression>(&AExpression::OpDict, subexprl, DebugInfoRange(@1, @5));
639                 $$ = new Value(make_shared<AExpression>(&AExpression::OpSetPlus, aindexl, expr, DebugInfoRange(@1, @5)));
640         }
641         | T_IMPORT rterm
642         {
643                 AExpression::Ptr avar = make_shared<AExpression>(&AExpression::OpVariable, "type", DebugInfoRange(@1, @2));
644                 $$ = new Value(make_shared<AExpression>(&AExpression::OpImport, avar, *$2, DebugInfoRange(@1, @2)));
645                 delete $2;
646         }
647         | T_ASSIGN T_WHERE rterm
648         {
649                 if (!(m_Apply || m_ObjectAssign))
650                         BOOST_THROW_EXCEPTION(ConfigError("'assign' keyword not valid in this context."));
651
652                 m_SeenAssign = true;
653
654                 m_Assign = make_shared<AExpression>(&AExpression::OpLogicalOr, m_Assign, *$3, DebugInfoRange(@1, @3));
655                 delete $3;
656
657                 $$ = new Value(make_shared<AExpression>(&AExpression::OpLiteral, Empty, DebugInfoRange(@1, @3)));
658         }
659         | T_IGNORE T_WHERE rterm
660         {
661                 if (!(m_Apply || m_ObjectAssign))
662                         BOOST_THROW_EXCEPTION(ConfigError("'ignore' keyword not valid in this context."));
663
664                 m_Ignore = make_shared<AExpression>(&AExpression::OpLogicalOr, m_Ignore, *$3, DebugInfoRange(@1, @3));
665
666                 delete $3;
667
668                 $$ = new Value(make_shared<AExpression>(&AExpression::OpLiteral, Empty, DebugInfoRange(@1, @3)));
669         }
670         | T_RETURN rterm
671         {
672                 AExpression::Ptr aname = make_shared<AExpression>(&AExpression::OpLiteral, "__result", @1);
673                 $$ = new Value(make_shared<AExpression>(&AExpression::OpSet, aname, *$2, DebugInfoRange(@1, @2)));
674                 delete $2;
675
676         }
677         | apply
678         {
679                 $$ = $1;
680         }
681         | object
682         {
683                 $$ = $1;
684         }
685         | rterm
686         {
687                 $$ = $1;
688         }
689         ;
690         
691 rterm_items: /* empty */
692         {
693                 $$ = new Array();
694         }
695         | rterm_items_inner
696         {
697                 $$ = $1;
698         }
699         | rterm_items_inner arraysep
700         {
701                 $$ = $1;
702         }
703         ;
704
705 rterm_items_inner: rterm
706         {
707                 $$ = new Array();
708                 $$->Add(*$1);
709                 delete $1;
710         }
711         | rterm_items_inner arraysep rterm
712         {
713                 $$ = $1;
714                 $$->Add(*$3);
715                 delete $3;
716         }
717         ;
718
719 rterm_array: '[' newlines rterm_items newlines ']'
720         {
721                 $$ = new Value(make_shared<AExpression>(&AExpression::OpArray, Array::Ptr($3), DebugInfoRange(@1, @5)));
722         }
723         | '[' newlines rterm_items ']'
724         {
725                 $$ = new Value(make_shared<AExpression>(&AExpression::OpArray, Array::Ptr($3), DebugInfoRange(@1, @4)));
726         }
727         | '[' rterm_items newlines ']'
728         {
729                 $$ = new Value(make_shared<AExpression>(&AExpression::OpArray, Array::Ptr($2), DebugInfoRange(@1, @4)));
730         }
731         | '[' rterm_items ']'
732         {
733                 $$ = new Value(make_shared<AExpression>(&AExpression::OpArray, Array::Ptr($2), DebugInfoRange(@1, @3)));
734         }
735         ;
736
737 rterm_scope: '{' newlines lterm_items newlines '}'
738         {
739                 $$ = new Value(make_shared<AExpression>(&AExpression::OpDict, Array::Ptr($3), DebugInfoRange(@1, @5)));
740         }
741         | '{' newlines lterm_items '}'
742         {
743                 $$ = new Value(make_shared<AExpression>(&AExpression::OpDict, Array::Ptr($3), DebugInfoRange(@1, @4)));
744         }
745         | '{' lterm_items newlines '}'
746         {
747                 $$ = new Value(make_shared<AExpression>(&AExpression::OpDict, Array::Ptr($2), DebugInfoRange(@1, @4)));
748         }
749         | '{' lterm_items '}'
750         {
751                 $$ = new Value(make_shared<AExpression>(&AExpression::OpDict, Array::Ptr($2), DebugInfoRange(@1, @3)));
752         }
753         ;
754
755 rterm: T_STRING
756         {
757                 $$ = new Value(make_shared<AExpression>(&AExpression::OpLiteral, $1, @1));
758                 free($1);
759         }
760         | T_NUMBER
761         {
762                 $$ = new Value(make_shared<AExpression>(&AExpression::OpLiteral, $1, @1));
763         }
764         | T_NULL
765         {
766                 $$ = new Value(make_shared<AExpression>(&AExpression::OpLiteral, Empty, @1));
767         }
768         | rterm '.' T_IDENTIFIER
769         {
770                 $$ = new Value(make_shared<AExpression>(&AExpression::OpIndexer, *$1, make_shared<AExpression>(&AExpression::OpLiteral, $3, @3), DebugInfoRange(@1, @3)));
771                 delete $1;
772                 free($3);
773         }
774         | rterm '(' rterm_items ')'
775         {
776                 Array::Ptr arguments = Array::Ptr($3);
777                 $$ = new Value(make_shared<AExpression>(&AExpression::OpFunctionCall, *$1, make_shared<AExpression>(&AExpression::OpLiteral, arguments, @3), DebugInfoRange(@1, @4)));
778                 delete $1;
779         }
780         | T_IDENTIFIER
781         {
782                 $$ = new Value(make_shared<AExpression>(&AExpression::OpVariable, $1, @1));
783                 free($1);
784         }
785         | '!' rterm
786         {
787                 $$ = new Value(make_shared<AExpression>(&AExpression::OpLogicalNegate, *$2, DebugInfoRange(@1, @2)));
788                 delete $2;
789         }
790         | '~' rterm
791         {
792                 $$ = new Value(make_shared<AExpression>(&AExpression::OpNegate, *$2, DebugInfoRange(@1, @2)));
793                 delete $2;
794         }
795         | rterm '[' rterm ']'
796         {
797                 $$ = new Value(make_shared<AExpression>(&AExpression::OpIndexer, *$1, *$3, DebugInfoRange(@1, @4)));
798                 delete $1;
799                 delete $3;
800         }
801         | rterm_array
802         {
803                 $$ = $1;
804         }
805         | rterm_scope
806         {
807                 $$ = $1;
808         }
809         | '('
810         {
811                 ignore_newlines++;
812         }
813         rterm ')'
814         {
815                 ignore_newlines--;
816                 $$ = $3;
817         }
818         | rterm T_LOGICAL_OR rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
819         | rterm T_LOGICAL_AND rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
820         | rterm T_BINARY_OR rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
821         | rterm T_BINARY_AND rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
822         | rterm T_IN rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
823         | rterm T_NOT_IN rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
824         | rterm T_EQUAL rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
825         | rterm T_NOT_EQUAL rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
826         | rterm T_LESS_THAN rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
827         | rterm T_LESS_THAN_OR_EQUAL rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
828         | rterm T_GREATER_THAN rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
829         | rterm T_GREATER_THAN_OR_EQUAL rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
830         | rterm T_SHIFT_LEFT rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
831         | rterm T_SHIFT_RIGHT rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
832         | rterm T_PLUS rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
833         | rterm T_MINUS rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
834         | rterm T_MULTIPLY rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
835         | rterm T_DIVIDE_OP rterm { MakeRBinaryOp(&$$, $2, $1, $3, @1, @3); }
836         | T_FUNCTION identifier '(' identifier_items ')' rterm_scope
837         {
838                 Array::Ptr arr = make_shared<Array>();
839
840                 arr->Add($2);
841                 free($2);
842
843                 AExpression::Ptr aexpr = *$6;
844                 delete $6;
845                 aexpr->MakeInline();
846                 arr->Add(aexpr);
847
848                 $$ = new Value(make_shared<AExpression>(&AExpression::OpFunction, arr, Array::Ptr($4), DebugInfoRange(@1, @6)));
849         }
850         | T_FUNCTION '(' identifier_items ')' rterm_scope
851         {
852                 Array::Ptr arr = make_shared<Array>();
853
854                 arr->Add(Empty);
855
856                 AExpression::Ptr aexpr = *$5;
857                 delete $5;
858                 aexpr->MakeInline();
859                 arr->Add(aexpr);
860
861                 $$ = new Value(make_shared<AExpression>(&AExpression::OpFunction, arr, Array::Ptr($3), DebugInfoRange(@1, @5)));
862         }
863         | T_LAMBDA identifier_items ':' rterm
864         {
865                 Array::Ptr arr = make_shared<Array>();
866
867                 arr->Add(Empty);
868
869                 Array::Ptr arrex = make_shared<Array>();
870                 arrex->Add(make_shared<AExpression>(&AExpression::OpSet, make_shared<AExpression>(&AExpression::OpLiteral, "__result", @4), *$4, @4));
871                 delete $4;
872                 AExpression::Ptr aexpr = make_shared<AExpression>(&AExpression::OpDict, arrex, true, @4);
873
874                 arr->Add(aexpr);
875
876                 $$ = new Value(make_shared<AExpression>(&AExpression::OpFunction, arr, Array::Ptr($2), DebugInfoRange(@1, @4)));
877         }
878         ;
879
880 target_type_specifier: /* empty */
881         {
882                 $$ = strdup("");
883         }
884         | T_TO identifier
885         {
886                 $$ = $2;
887         }
888         ;
889
890 apply:
891         {
892                 m_Apply = true;
893                 m_SeenAssign = false;
894                 m_Assign = make_shared<AExpression>(&AExpression::OpLiteral, false, DebugInfo());
895                 m_Ignore = make_shared<AExpression>(&AExpression::OpLiteral, false, DebugInfo());
896         }
897         T_APPLY identifier rterm target_type_specifier rterm
898         {
899                 m_Apply = false;
900
901                 String type = $3;
902                 free($3);
903                 AExpression::Ptr aname = *$4;
904                 delete $4;
905                 String target = $5;
906                 free($5);
907
908                 if (!ApplyRule::IsValidSourceType(type))
909                         BOOST_THROW_EXCEPTION(ConfigError("'apply' cannot be used with type '" + type + "'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
910
911                 if (!ApplyRule::IsValidTargetType(type, target)) {
912                         if (target == "") {
913                                 std::vector<String> types = ApplyRule::GetTargetTypes(type);
914                                 String typeNames;
915
916                                 for (int i = 0; i < types.size(); i++) {
917                                         if (typeNames != "") {
918                                                 if (i == types.size() - 1)
919                                                         typeNames += " or ";
920                                                 else
921                                                         typeNames += ", ";
922                                         }
923
924                                         typeNames += "'" + types[i] + "'";
925                                 }
926
927                                 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)));
928                         } else
929                                 BOOST_THROW_EXCEPTION(ConfigError("'apply' target type '" + target + "' is invalid") << errinfo_debuginfo(DebugInfoRange(@2, @5)));
930                 }
931
932                 AExpression::Ptr exprl = *$6;
933                 delete $6;
934
935                 exprl->MakeInline();
936
937                 // assign && !ignore
938                 if (!m_SeenAssign)
939                         BOOST_THROW_EXCEPTION(ConfigError("'apply' is missing 'assign'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
940
941                 AExpression::Ptr rex = make_shared<AExpression>(&AExpression::OpLogicalNegate, m_Ignore, DebugInfoRange(@2, @5));
942                 AExpression::Ptr filter = make_shared<AExpression>(&AExpression::OpLogicalAnd, m_Assign, rex, DebugInfoRange(@2, @5));
943
944                 Array::Ptr args = make_shared<Array>();
945                 args->Add(type);
946                 args->Add(target);
947                 args->Add(aname);
948                 args->Add(filter);
949
950                 $$ = new Value(make_shared<AExpression>(&AExpression::OpApply, args, exprl, DebugInfoRange(@2, @5)));
951
952                 m_Assign.reset();
953                 m_Ignore.reset();
954         }
955         ;
956
957 newlines: T_NEWLINE
958         | newlines T_NEWLINE
959         ;
960
961 /* required separator */
962 sep: ',' newlines
963         | ','
964         | ';' newlines
965         | ';'
966         | newlines
967         ;
968
969 arraysep: ',' newlines
970         | ','
971         ;
972
973 %%