1 /*
2   Copyright 2020 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 %{
26 #include <cf3parse_logic.h>
27 %}
28 
29 %token IDENTIFIER QUOTED_STRING CLASS_GUARD PROMISE_GUARD BUNDLE BODY PROMISE FAT_ARROW THIN_ARROW NAKEDVAR
30 %expect 1
31 
32 %%
33 
34 specification:       /* empty */
35                      | blocks
36 
37 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
38 
39 blocks:                block
40                      | blocks block;
41 
42 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
43 
44 block:                 bundle
45                      | body
46                      | promise
47                      | error
48                        {
49                            ParseError("Expected 'bundle' or 'body' keyword, wrong input '%s'", yytext);
50                            YYABORT;
51                        }
52 
53 bundle:                BUNDLE bundletype bundleid arglist bundlebody
54 
55 body:                  BODY bodytype bodyid arglist bodybody
56 
57 promise:               PROMISE promisecomponent promiseid arglist bodybody
58 
59 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
60 
61 bundletype:            bundletype_values
62                        {
63                            ParserBeginBlock(PARSER_BLOCK_BUNDLE);
64                        }
65 
66 bundletype_values:     typeid
67                        {
68                            /* FIXME: We keep it here, because we skip unknown
69                             * promise bundles. Ought to be moved to
70                             * after-parsing step once we know how to deal with
71                             * it */
72 
73                            if (!BundleTypeCheck(P.blocktype))
74                            {
75                                ParseError("Unknown bundle type '%s'", P.blocktype);
76                                INSTALL_SKIP = true;
77                            }
78                        }
79                      | error
80                        {
81                            yyclearin;
82                            ParseError("Expected bundle type, wrong input '%s'", yytext);
83                            INSTALL_SKIP = true;
84                        }
85 
86 bundleid:              bundleid_values
87                        {
88                           ParserDebug("\tP:bundle:%s:%s\n", P.blocktype, P.blockid);
89                           CURRENT_BLOCKID_LINE = P.line_no;
90                        }
91 
92 bundleid_values:       symbol
93                      | error
94                        {
95                            yyclearin;
96                            ParseError("Expected bundle identifier, wrong input '%s'", yytext);
97                            INSTALL_SKIP = true;
98                        }
99 
100 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
101 
102 bodytype:              bodytype_values
103                        {
104                            ParserBeginBlock(PARSER_BLOCK_BODY);
105                        }
106 
107 bodytype_values:       typeid
108                        {
109                            if (!BodySyntaxGet(PARSER_BLOCK_BODY, P.blocktype))
110                            {
111                                ParseError("Unknown body type '%s'", P.blocktype);
112                            }
113                        }
114                      | error
115                        {
116                            yyclearin;
117                            ParseError("Expected body type, wrong input '%s'", yytext);
118                        }
119 
120 bodyid:                bodyid_values
121                        {
122                           ParserDebug("\tP:body:%s:%s\n", P.blocktype, P.blockid);
123                           CURRENT_BLOCKID_LINE = P.line_no;
124                        }
125 
126 bodyid_values:         symbol
127                      | error
128                        {
129                            yyclearin;
130                            ParseError("Expected body identifier, wrong input '%s'", yytext);
131                            INSTALL_SKIP = true;
132                        }
133 
134 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
135 
136 promisecomponent:      promisecomponent_values
137                        {
138                            ParserBeginBlock(PARSER_BLOCK_PROMISE);
139                        }
140 
141 promisecomponent_values: typeid
142                          {
143                              if (!StringEqual(P.blocktype, "agent"))
144                              {
145                                  ParseError("Custom promises only supported for 'agent', not '%s'", P.blocktype);
146                              }
147                          }
148                        | error
149                          {
150                              yyclearin;
151                              ParseError("Expected 'agent', got '%s'", yytext);
152                          }
153 
154 promiseid:             promiseid_values
155                        {
156                           ParserDebug("\tP:promise:%s:%s\n", P.blocktype, P.blockid);
157                           CURRENT_BLOCKID_LINE = P.line_no;
158                        }
159 
160 promiseid_values:      symbol
161                      | error
162                        {
163                            yyclearin;
164                            ParseError("Expected promise type identifier, wrong input '%s'", yytext);
165                            INSTALL_SKIP = true;
166                        }
167 
168 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
169 
170 typeid:                IDENTIFIER
171                        {
172                            strncpy(P.blocktype,P.currentid,CF_MAXVARSIZE);
173 
174                            RlistDestroy(P.useargs);
175                            P.useargs = NULL;
176                        }
177 
178 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
179 
180 symbol:                IDENTIFIER
181                        {
182                            strncpy(P.blockid,P.currentid,CF_MAXVARSIZE);
183                            P.offsets.last_block_id = P.offsets.last_id;
184                        };
185 
186 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
187 
188 arglist:               /* Empty */
189                      | arglist_begin aitems arglist_end
190                      | arglist_begin arglist_end
191                      | arglist_begin error
192                        {
193                           yyclearin;
194                           ParseError("Error in bundle parameter list, expected ')', wrong input '%s'", yytext);
195                        }
196 
197 arglist_begin:         '('
198                        {
199                            ParserDebug("P:%s:%s:%s arglist begin:%s\n", ParserBlockString(P.block),P.blocktype,P.blockid, yytext);
200                        }
201 
202 arglist_end:           ')'
203                        {
204                            ParserDebug("P:%s:%s:%s arglist end:%s\n", ParserBlockString(P.block),P.blocktype,P.blockid, yytext);
205                        }
206 
207 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
208 
209 aitems:                aitem
210                      | aitems ',' aitem
211 
212 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
213 
214 aitem:                 IDENTIFIER  /* recipient of argument is never a literal */
215                        {
216                            ParserDebug("P:%s:%s:%s  arg id: %s\n", ParserBlockString(P.block),P.blocktype,P.blockid, P.currentid);
217                            RlistAppendScalar(&(P.useargs),P.currentid);
218                        }
219                      | error
220                        {
221                           yyclearin;
222                           ParseError("Expected identifier, wrong input '%s'", yytext);
223                        }
224 
225 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
226 
227 bundlebody:            body_begin
228                        {
229                            ParserBeginBundleBody();
230                        }
231 
232                        bundle_decl
233 
234                        '}'
235                        {
236                            INSTALL_SKIP = false;
237                            ParserEndCurrentBlock();
238                        }
239 
240 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
241 
242 body_begin:            '{'
243                        {
244                            ParserDebug("P:%s:%s:%s begin body open\n", ParserBlockString(P.block),P.blocktype,P.blockid);
245                        }
246                      | error
247                        {
248                            ParseError("Expected body open '{', wrong input '%s'", yytext);
249                        }
250 
251 
252 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
253 
254 bundle_decl:           /* empty */
255                      | bundle_statements
256 
257 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
258 
259 bundle_statements:     bundle_statement
260                      | bundle_statements bundle_statement
261                      | error
262                        {
263                           INSTALL_SKIP = true;
264                           ParseError("Expected promise type, got '%s'", yytext);
265                           ParserDebug("P:promise_type:error yychar = %d, %c, yyempty = %d\n", yychar, yychar, YYEMPTY);
266                           yyclearin;
267                        }
268 
269 
270 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
271 
272 bundle_statement:      promise_guard classpromises_decl
273 
274 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
275 
276 promise_guard:         PROMISE_GUARD             /* BUNDLE ONLY */
277                        {
278                            ParserHandlePromiseGuard();
279                        }
280 
281 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
282 
283 classpromises_decl:    /* empty */
284                      | classpromises
285 
286 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
287 
288 classpromises:         classpromise
289                      | classpromises classpromise
290 
291 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
292 
293 classpromise:          class
294                      | promise_decl
295 
296 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
297 
298 
299 promise_decl:          promise_line ';'
300                      | promiser error
301                        {
302                            /*
303                             * Based on yychar display right error message
304                            */
305                            ParserDebug("P:promiser:error yychar = %d\n", yychar);
306                            if (yychar =='-' || yychar == '>')
307                            {
308                               ParseError("Expected '->', got '%s'", yytext);
309                            }
310                            else if (yychar == IDENTIFIER)
311                            {
312                               ParseError("Expected attribute, got '%s'", yytext);
313                            }
314                            else if (yychar == ',')
315                            {
316                               ParseError("Expected attribute, got '%s' (comma after promiser is not allowed since 3.5.0)", yytext);
317                            }
318                            else
319                            {
320                               ParseError("Expected ';', got '%s'", yytext);
321                            }
322                            yyclearin;
323                        }
324 
325 promise_line:           promise_with_promisee
326                       | promise_without_promisee
327 
328 
329 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
330 
331 promise_with_promisee: promiser
332 
333                        promisee_arrow
334 
335                        rval
336                        {
337                            if (!INSTALL_SKIP)
338                            {
339                                if (!P.currentstype)
340                                {
341                                    ParseError("Missing promise type declaration");
342                                }
343 
344                                P.currentpromise = BundleSectionAppendPromise(P.currentstype, P.promiser,
345                                                                              RvalCopy(P.rval),
346                                                                              P.currentclasses ? P.currentclasses : "any",
347                                                                              P.currentvarclasses);
348                                P.currentpromise->offset.line = CURRENT_PROMISER_LINE;
349                                P.currentpromise->offset.start = P.offsets.last_string;
350                                P.currentpromise->offset.context = P.offsets.last_class_id;
351                            }
352                            else
353                            {
354                                P.currentpromise = NULL;
355                            }
356                        }
357 
358                        promise_decl_constraints
359 
360 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
361 
362 promise_without_promisee: promiser
363                        {
364 
365                            if (!INSTALL_SKIP)
366                            {
367                                if (!P.currentstype)
368                                {
369                                    ParseError("Missing promise type declaration");
370                                }
371 
372                                P.currentpromise = BundleSectionAppendPromise(P.currentstype, P.promiser,
373                                                                              (Rval) { NULL, RVAL_TYPE_NOPROMISEE },
374                                                                              P.currentclasses ? P.currentclasses : "any",
375                                                                              P.currentvarclasses);
376                                P.currentpromise->offset.line = CURRENT_PROMISER_LINE;
377                                P.currentpromise->offset.start = P.offsets.last_string;
378                                P.currentpromise->offset.context = P.offsets.last_class_id;
379                            }
380                            else
381                            {
382                                P.currentpromise = NULL;
383                            }
384                        }
385 
386                        promise_decl_constraints
387 
388 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
389 
390 promiser:              QUOTED_STRING
391                        {
392                            if (P.promiser)
393                            {
394                                free(P.promiser);
395                            }
396                            P.promiser = P.currentstring;
397                            P.currentstring = NULL;
398                            CURRENT_PROMISER_LINE = P.line_no;
399                            ParserDebug("\tP:%s:%s:%s:%s:%s promiser = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currenttype, P.currentclasses ? P.currentclasses : "any", P.promiser);
400                        }
401                      | error
402                        {
403                           INSTALL_SKIP = true;
404                           ParserDebug("P:promiser:qstring::error yychar = %d\n", yychar);
405 
406                           if (yychar == BUNDLE || yychar == BODY)
407                           {
408                              ParseError("Expected '}', got '%s'", yytext);
409                              /*
410                              YYABORT;
411                              */
412                           }
413                           else
414                           {
415                              ParseError("Expected promiser string, got '%s'", yytext);
416                           }
417 
418                           yyclearin;
419                        }
420 
421 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
422 
423 promise_decl_constraints:       /* empty */
424                               | constraints_decl
425                               | constraints_decl error
426                                 {
427                                    /*
428                                     * Based on next token id display right error message
429                                    */
430                                    ParserDebug("P:constraints_decl:error yychar = %d\n", yychar);
431                                    if ( yychar == IDENTIFIER )
432                                    {
433                                        ParseError("Check previous line, Expected ',', got '%s'", yytext);
434                                    }
435                                    else
436                                    {
437                                        ParseError("Check previous line, Expected ';', got '%s'", yytext);
438                                    }
439                                    yyclearin;
440 
441                                 }
442 
443 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
444 
445 
446 constraints_decl:      constraints
447                        {
448                            /* Don't free these */
449                            strcpy(P.currentid,"");
450                            RlistDestroy(P.currentRlist);
451                            P.currentRlist = NULL;
452                            free(P.promiser);
453                            if (P.currentstring)
454                            {
455                                free(P.currentstring);
456                            }
457                            P.currentstring = NULL;
458                            P.promiser = NULL;
459                            P.promisee = NULL;
460                            /* reset argptrs etc*/
461                        }
462 
463 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
464 
465 constraints:           constraint                           /* BUNDLE ONLY */
466                      | constraints ',' constraint
467 
468 
469 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
470 
471 constraint:            constraint_id                        /* BUNDLE ONLY */
472                        assign_arrow
473                        rval
474                        {
475                            ParserHandleBundlePromiseRval();
476                        }
477 
478 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
479 
480 constraint_id:         IDENTIFIER                        /* BUNDLE ONLY */
481                        {
482                            ParserDebug("\tP:%s:%s:%s:%s:%s:%s attribute = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currenttype, P.currentclasses ? P.currentclasses : "any", P.promiser, P.currentid);
483 
484                            if (!PolicyHasCustomPromiseType(P.policy, P.currenttype))
485                            {
486                                const PromiseTypeSyntax *promise_type_syntax = PromiseTypeSyntaxGet(P.blocktype, P.currenttype);
487                                if (!promise_type_syntax)
488                                {
489                                    ParseError("Invalid promise type '%s' in bundle '%s' of type '%s'", P.currenttype, P.blockid, P.blocktype);
490                                    INSTALL_SKIP = true;
491                                }
492                                else if (!PromiseTypeSyntaxGetConstraintSyntax(promise_type_syntax, P.currentid))
493                                {
494                                    ParseError("Unknown attribute '%s' for promise type '%s' in bundle with type '%s'", P.currentid, P.currenttype, P.blocktype);
495                                    INSTALL_SKIP = true;
496                                }
497                            }
498 
499                            strncpy(P.lval,P.currentid,CF_MAXVARSIZE);
500                            RlistDestroy(P.currentRlist);
501                            P.currentRlist = NULL;
502                        }
503                      | error
504                        {
505                              ParseError("Expected attribute, got '%s'\n", yytext);
506                        }
507 
508 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
509 
510 bodybody:              body_begin
511                        {
512                            ParserBeginBlockBody();
513                        }
514 
515                        bodybody_inner
516 
517                        '}'
518                        {
519                            ParserEndCurrentBlock();
520                        }
521 
522 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
523 
524 bodybody_inner:        /* empty */
525                      | bodyattribs
526 
527 bodyattribs:           bodyattrib                    /* BODY/PROMISE ONLY */
528                      | bodyattribs bodyattrib
529 
530 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
531 
532 bodyattrib:            class
533                      | selection_line
534 
535 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
536 
537 selection_line:        selection ';'
538                      | selection error
539                        {
540                           ParseError("Expected ';' check previous statement, got '%s'", yytext);
541                        }
542 
543 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
544 
545 selection:             selection_id                         /* BODY/PROMISE ONLY */
546                        assign_arrow
547                        rval
548                        {
549                            ParserHandleBlockAttributeRval();
550                        }
551 
552 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
553 
554 selection_id:          IDENTIFIER
555                        {
556                            ParserDebug("\tP:%s:%s:%s:%s attribute = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentid);
557 
558                            if (!INSTALL_SKIP)
559                            {
560                                const BodySyntax *body_syntax = BodySyntaxGet(P.block, P.currentbody->type);
561 
562                                if (!body_syntax || !BodySyntaxGetConstraintSyntax(body_syntax->constraints, P.currentid))
563                                {
564                                    ParseError(
565                                        "Unknown attribute '%s' for '%s %s %s'",
566                                        P.currentid,                // attribute name (lval)
567                                        ParserBlockString(P.block), // body     (block type)
568                                        P.currentbody->type,        // file     (body type)
569                                        P.blockid);                 // control  (body name)
570                                    INSTALL_SKIP = true;
571                                }
572 
573                                strncpy(P.lval,P.currentid,CF_MAXVARSIZE);
574                            }
575                            RlistDestroy(P.currentRlist);
576                            P.currentRlist = NULL;
577                        }
578                      | error
579                        {
580                           ParserDebug("P:selection_id:idsyntax:error yychar = %d\n", yychar);
581 
582                           if ( yychar == BUNDLE || yychar == BODY )
583                           {
584                              ParseError("Expected '}', got '%s'", yytext);
585                              /*
586                              YYABORT;
587                              */
588                           }
589                           else
590                           {
591                              ParseError("Expected attribute, got '%s'", yytext);
592                           }
593 
594                           yyclearin;
595                        }
596 
597 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
598 
599 assign_arrow:          FAT_ARROW
600                        {
601                            ParserDebug("\tP:=>\n");
602                        }
603                      | error
604                        {
605                           yyclearin;
606                           ParseError("Expected '=>', got '%s'", yytext);
607                        }
608 
609 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
610 
611 promisee_arrow:        THIN_ARROW
612                        {
613                            ParserDebug("\tP:->\n");
614                        }
615                        /* else we display the wrong error
616                      | error
617                        {
618                           yyclearin;
619                           ParseError("Expected '->', got '%s'", yytext);
620                        }
621                        */
622 
623 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
624 
625 class:                 CLASS_GUARD
626                        {
627                            P.offsets.last_class_id = P.offsets.current - strlen(P.currentclasses ? P.currentclasses : P.currentvarclasses) - 2;
628                            ParserDebug("\tP:%s:%s:%s:%s %s = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currenttype, P.currentclasses ? "class": "varclass", yytext);
629 
630                            if (P.currentclasses != NULL)
631                            {
632                                char *literal = xstrdup(P.currentclasses);
633 
634                                ValidateClassLiteral(literal);
635 
636                                free(literal);
637                            }
638                        }
639 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
640 
641 
642 
643 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
644 
645 rval:                  IDENTIFIER
646                        {
647                            ParserDebug("\tP:%s:%s:%s:%s id rval, %s = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.lval, P.currentid);
648                            RvalDestroy(P.rval);
649                            P.rval = (Rval) { xstrdup(P.currentid), RVAL_TYPE_SCALAR };
650                            P.references_body = true;
651                        }
652                      | QUOTED_STRING
653                        {
654                            ParserDebug("\tP:%s:%s:%s:%s qstring rval, %s = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.lval, P.currentstring);
655                            RvalDestroy(P.rval);
656                            P.rval = (Rval) { P.currentstring, RVAL_TYPE_SCALAR };
657 
658                            P.currentstring = NULL;
659                            P.references_body = false;
660 
661                            if (P.currentpromise)
662                            {
663                                if (LvalWantsBody(P.currentpromise->parent_section->promise_type, P.lval))
664                                {
665                                    yyerror("An rvalue is quoted, but we expect an unquoted body identifier");
666                                }
667                            }
668                        }
669                      | NAKEDVAR
670                        {
671                            ParserDebug("\tP:%s:%s:%s:%s nakedvar rval, %s = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.lval, P.currentstring);
672                            RvalDestroy(P.rval);
673                            P.rval = (Rval) { P.currentstring, RVAL_TYPE_SCALAR };
674 
675                            P.currentstring = NULL;
676                            P.references_body = false;
677                        }
678                      | list
679                        {
680                            ParserDebug("\tP:%s:%s:%s:%s install list =  %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.lval);
681                            RvalDestroy(P.rval);
682                            P.rval = (Rval) { RlistCopy(P.currentRlist), RVAL_TYPE_LIST };
683                            RlistDestroy(P.currentRlist);
684                            P.currentRlist = NULL;
685                            P.references_body = false;
686                        }
687                      | usefunction
688                        {
689                            RvalDestroy(P.rval);
690                            P.rval = (Rval) { P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL };
691                            P.currentfncall[P.arg_nesting+1] = NULL;
692                            P.references_body = false;
693                        }
694 
695                      | error
696                        {
697                            yyclearin;
698                            ParseError("Invalid r-value type '%s'", yytext);
699                        }
700 
701 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
702 
703 list:                  '{' '}'
704                      | '{' litems '}'
705                      | '{' litems ',' '}'
706 
707 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
708 
709 litems:
710                        litem
711                      | litems ',' litem
712                      | litem error
713                        {
714                            ParserDebug("P:rval:list:error yychar = %d\n", yychar);
715                            if ( yychar ==';' )
716                            {
717                                ParseError("Expected '}', wrong input '%s'", yytext);
718                            }
719                            else if ( yychar == FAT_ARROW )
720                            {
721                                ParseError("Check list statement previous line,"
722                                           " Expected '}', wrong input '%s'",
723                                           yytext);
724                            }
725                            else
726                            {
727                                ParseError("Expected ',', wrong input '%s'", yytext);
728                            }
729                            yyclearin;
730                        }
731 
732 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
733 
734 litem:                 IDENTIFIER
735                        {
736                            ParserDebug("\tP:%s:%s:%s:%s list append: "
737                                        "id = %s\n",
738                                        ParserBlockString(P.block), P.blocktype, P.blockid,
739                                        (P.currentclasses ?
740                                             P.currentclasses : "any"),
741                                        P.currentid);
742                            RlistAppendScalar((Rlist **) &P.currentRlist,
743                                              P.currentid);
744                        }
745 
746                      | QUOTED_STRING
747                        {
748                            ParserDebug("\tP:%s:%s:%s:%s list append: "
749                                        "qstring = %s\n",
750                                        ParserBlockString(P.block), P.blocktype, P.blockid,
751                                        (P.currentclasses ?
752                                             P.currentclasses : "any"),
753                                        P.currentstring);
754                            RlistAppendScalar((Rlist **) &P.currentRlist,
755                                              (void *) P.currentstring);
756                            free(P.currentstring);
757                            P.currentstring = NULL;
758                        }
759 
760                      | NAKEDVAR
761                        {
762                            ParserDebug("\tP:%s:%s:%s:%s list append: nakedvar = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentstring);
763                            RlistAppendScalar((Rlist **)&P.currentRlist,(void *)P.currentstring);
764                            free(P.currentstring);
765                            P.currentstring = NULL;
766                        }
767 
768                      | usefunction
769                        {
770                            RlistAppend(&P.currentRlist, P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL);
771                            FnCallDestroy(P.currentfncall[P.arg_nesting+1]);
772                            P.currentfncall[P.arg_nesting+1] = NULL;
773                        }
774 
775                      | error
776                        {
777                           yyclearin;
778                           ParseError("Invalid input for a list item, got '%s'", yytext);
779                        }
780 
781 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
782 
783 functionid:            IDENTIFIER
784                        {
785                            ParserDebug("\tP:%s:%s:%s:%s function id = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentid);
786                        }
787                      | NAKEDVAR
788                        {
789                            ParserDebug("\tP:%s:%s:%s:%s function nakedvar = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentstring);
790                            strncpy(P.currentid, P.currentstring, CF_MAXVARSIZE - 1); // Make a var look like an ID
791                            free(P.currentstring);
792                            P.currentstring = NULL;
793                        }
794 
795 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
796 
797 
798 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
799 
800 usefunction:           functionid givearglist
801                        {
802                            ParserDebug("\tP:%s:%s:%s:%s Finished with function, now at level %d\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.arg_nesting);
803                        };
804 
805 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
806 
807 givearglist:           '('
808                        {
809                            if (++P.arg_nesting >= CF_MAX_NESTING)
810                            {
811                                fatal_yyerror("Nesting of functions is deeper than recommended");
812                            }
813                            P.currentfnid[P.arg_nesting] = xstrdup(P.currentid);
814                            ParserDebug("\tP:%s:%s:%s begin givearglist for function %s, level %d\n", ParserBlockString(P.block),P.blocktype,P.blockid, P.currentfnid[P.arg_nesting], P.arg_nesting );
815                        }
816 
817                        gaitems
818 
819                        ')'
820                        {
821                            ParserDebug("\tP:%s:%s:%s end givearglist for function %s, level %d\n", ParserBlockString(P.block),P.blocktype,P.blockid, P.currentfnid[P.arg_nesting], P.arg_nesting );
822                            P.currentfncall[P.arg_nesting] = FnCallNew(P.currentfnid[P.arg_nesting], P.giveargs[P.arg_nesting]);
823                            P.giveargs[P.arg_nesting] = NULL;
824                            strcpy(P.currentid,"");
825                            free(P.currentfnid[P.arg_nesting]);
826                            P.currentfnid[P.arg_nesting] = NULL;
827                            P.arg_nesting--;
828                        }
829 
830 
831 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
832 
833 gaitems:               /* empty */
834                      | gaitem
835                      | gaitems ',' gaitem
836                      | gaitem error
837                        {
838                            ParseError("Expected ',', wrong input '%s'", yytext);
839                            yyclearin;
840                        }
841 
842 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
843 
844 gaitem:                IDENTIFIER
845                        {
846                            ParserDebug("\tP:%s:%s:%s:%s function %s, id arg = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentfnid[P.arg_nesting], P.currentid);
847                            /* currently inside a use function */
848                            RlistAppendScalar(&P.giveargs[P.arg_nesting],P.currentid);
849                        }
850 
851                      | QUOTED_STRING
852                        {
853                            /* currently inside a use function */
854                            ParserDebug("\tP:%s:%s:%s:%s function %s, qstring arg = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentfnid[P.arg_nesting], P.currentstring);
855                            RlistAppendScalar(&P.giveargs[P.arg_nesting],P.currentstring);
856                            free(P.currentstring);
857                            P.currentstring = NULL;
858                        }
859 
860                      | NAKEDVAR
861                        {
862                            /* currently inside a use function */
863                            ParserDebug("\tP:%s:%s:%s:%s function %s, nakedvar arg = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentfnid[P.arg_nesting], P.currentstring);
864                            RlistAppendScalar(&P.giveargs[P.arg_nesting],P.currentstring);
865                            free(P.currentstring);
866                            P.currentstring = NULL;
867                        }
868 
869                      | usefunction
870                        {
871                            /* Careful about recursion */
872                            ParserDebug("\tP:%s:%s:%s:%s function %s, nakedvar arg = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentfnid[P.arg_nesting], P.currentstring);
873                            RlistAppend(&P.giveargs[P.arg_nesting], P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL);
874                            RvalDestroy((Rval) { P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL });
875                            P.currentfncall[P.arg_nesting+1] = NULL;
876                        }
877 
878                      | error
879                        {
880                            ParserDebug("P:rval:function:gaitem:error yychar = %d\n", yychar);
881                            if (yychar == ';')
882                            {
883                               ParseError("Expected ')', wrong input '%s'", yytext);
884                            }
885                            else if (yychar == FAT_ARROW )
886                            {
887                               ParseError("Check function statement  previous line, Expected ')', wrong input '%s'", yytext);
888                            }
889                            else
890                            {
891                               ParseError("Invalid function argument, wrong input '%s'", yytext);
892                            }
893                            yyclearin;
894                        }
895 
896 %%
897