1 /*
2   Copyright 2021 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                           if (IsBuiltInPromiseType(P.blockid))
157                           {
158                              ParseError("'%s' promises are built in and cannot be custom", yytext);
159                           }
160                           ParserDebug("\tP:promise:%s:%s\n", P.blocktype, P.blockid);
161                           CURRENT_BLOCKID_LINE = P.line_no;
162                        }
163 
164 promiseid_values:      symbol
165                      | error
166                        {
167                            yyclearin;
168                            ParseError("Expected promise type identifier, wrong input '%s'", yytext);
169                            INSTALL_SKIP = true;
170                        }
171 
172 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
173 
174 typeid:                IDENTIFIER
175                        {
176                            strncpy(P.blocktype,P.currentid,CF_MAXVARSIZE);
177 
178                            RlistDestroy(P.useargs);
179                            P.useargs = NULL;
180                        }
181 
182 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
183 
184 symbol:                IDENTIFIER
185                        {
186                            strncpy(P.blockid,P.currentid,CF_MAXVARSIZE);
187                            P.offsets.last_block_id = P.offsets.last_id;
188                        };
189 
190 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
191 
192 arglist:               /* Empty */
193                      | arglist_begin aitems arglist_end
194                      | arglist_begin arglist_end
195                      | arglist_begin error
196                        {
197                           yyclearin;
198                           ParseError("Error in bundle parameter list, expected ')', wrong input '%s'", yytext);
199                        }
200 
201 arglist_begin:         '('
202                        {
203                            ParserDebug("P:%s:%s:%s arglist begin:%s\n", ParserBlockString(P.block),P.blocktype,P.blockid, yytext);
204                        }
205 
206 arglist_end:           ')'
207                        {
208                            ParserDebug("P:%s:%s:%s arglist end:%s\n", ParserBlockString(P.block),P.blocktype,P.blockid, yytext);
209                        }
210 
211 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
212 
213 aitems:                aitem
214                      | aitem ','
215                      | aitem ',' aitems
216 
217 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
218 
219 aitem:                 IDENTIFIER  /* recipient of argument is never a literal */
220                        {
221                            ParserDebug("P:%s:%s:%s  arg id: %s\n", ParserBlockString(P.block),P.blocktype,P.blockid, P.currentid);
222                            RlistAppendScalar(&(P.useargs),P.currentid);
223                        }
224                      | error
225                        {
226                           yyclearin;
227                           ParseError("Expected identifier, wrong input '%s'", yytext);
228                        }
229 
230 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
231 
232 bundlebody:            body_begin
233                        {
234                            ParserBeginBundleBody();
235                        }
236 
237                        bundle_decl
238 
239                        '}'
240                        {
241                            INSTALL_SKIP = false;
242                            ParserEndCurrentBlock();
243                        }
244 
245 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
246 
247 body_begin:            '{'
248                        {
249                            ParserDebug("P:%s:%s:%s begin body open\n", ParserBlockString(P.block),P.blocktype,P.blockid);
250                        }
251                      | error
252                        {
253                            ParseError("Expected body open '{', wrong input '%s'", yytext);
254                        }
255 
256 
257 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
258 
259 bundle_decl:           /* empty */
260                      | bundle_statements
261 
262 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
263 
264 bundle_statements:     bundle_statement
265                      | bundle_statements bundle_statement
266                      | error
267                        {
268                           INSTALL_SKIP = true;
269                           ParseError("Expected promise type, got '%s'", yytext);
270                           ParserDebug("P:promise_type:error yychar = %d, %c, yyempty = %d\n", yychar, yychar, YYEMPTY);
271                           yyclearin;
272                        }
273 
274 
275 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
276 
277 bundle_statement:      promise_guard classpromises_decl
278 
279 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
280 
281 promise_guard:         PROMISE_GUARD             /* BUNDLE ONLY */
282                        {
283                            ParserHandlePromiseGuard();
284                        }
285 
286 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
287 
288 classpromises_decl:    /* empty */
289                      | classpromises
290 
291 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
292 
293 classpromises:         classpromise
294                      | classpromises classpromise
295 
296 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
297 
298 classpromise:          class
299                      | promise_decl
300 
301 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
302 
303 
304 promise_decl:          promise_line ';'
305                      | promiser error
306                        {
307                            /*
308                             * Based on yychar display right error message
309                            */
310                            ParserDebug("P:promiser:error yychar = %d\n", yychar);
311                            if (yychar =='-' || yychar == '>')
312                            {
313                               ParseError("Expected '->', got '%s'", yytext);
314                            }
315                            else if (yychar == IDENTIFIER)
316                            {
317                               ParseError("Expected attribute, got '%s'", yytext);
318                            }
319                            else if (yychar == ',')
320                            {
321                               ParseError("Expected attribute, got '%s' (comma after promiser is not allowed since 3.5.0)", yytext);
322                            }
323                            else
324                            {
325                               ParseError("Expected ';', got '%s'", yytext);
326                            }
327                            yyclearin;
328                        }
329 
330 promise_line:           promise_with_promisee
331                       | promise_without_promisee
332 
333 
334 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
335 
336 promise_with_promisee: promiser
337 
338                        promisee_arrow
339 
340                        rval
341                        {
342                            if (!INSTALL_SKIP)
343                            {
344                                if (!P.currentstype)
345                                {
346                                    ParseError("Missing promise type declaration");
347                                }
348 
349                                P.currentpromise = BundleSectionAppendPromise(P.currentstype, P.promiser,
350                                                                              RvalCopy(P.rval),
351                                                                              P.currentclasses ? P.currentclasses : "any",
352                                                                              P.currentvarclasses);
353                                P.currentpromise->offset.line = CURRENT_PROMISER_LINE;
354                                P.currentpromise->offset.start = P.offsets.last_string;
355                                P.currentpromise->offset.context = P.offsets.last_class_id;
356                            }
357                            else
358                            {
359                                P.currentpromise = NULL;
360                            }
361                        }
362 
363                        promise_decl_constraints
364 
365 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
366 
367 promise_without_promisee: promiser
368                        {
369 
370                            if (!INSTALL_SKIP)
371                            {
372                                if (!P.currentstype)
373                                {
374                                    ParseError("Missing promise type declaration");
375                                }
376 
377                                P.currentpromise = BundleSectionAppendPromise(P.currentstype, P.promiser,
378                                                                              (Rval) { NULL, RVAL_TYPE_NOPROMISEE },
379                                                                              P.currentclasses ? P.currentclasses : "any",
380                                                                              P.currentvarclasses);
381                                P.currentpromise->offset.line = CURRENT_PROMISER_LINE;
382                                P.currentpromise->offset.start = P.offsets.last_string;
383                                P.currentpromise->offset.context = P.offsets.last_class_id;
384                            }
385                            else
386                            {
387                                P.currentpromise = NULL;
388                            }
389                        }
390 
391                        promise_decl_constraints
392 
393 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
394 
395 promiser:              QUOTED_STRING
396                        {
397                            if (P.promiser)
398                            {
399                                free(P.promiser);
400                            }
401                            P.promiser = P.currentstring;
402                            P.currentstring = NULL;
403                            CURRENT_PROMISER_LINE = P.line_no;
404                            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);
405                        }
406                      | error
407                        {
408                           INSTALL_SKIP = true;
409                           ParserDebug("P:promiser:qstring::error yychar = %d\n", yychar);
410 
411                           if (yychar == BUNDLE || yychar == BODY)
412                           {
413                              ParseError("Expected '}', got '%s'", yytext);
414                              /*
415                              YYABORT;
416                              */
417                           }
418                           else
419                           {
420                              ParseError("Expected promiser string, got '%s'", yytext);
421                           }
422 
423                           yyclearin;
424                        }
425 
426 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
427 
428 promise_decl_constraints:       /* empty */
429                               | constraints_decl
430                               | constraints_decl error
431                                 {
432                                    /*
433                                     * Based on next token id display right error message
434                                    */
435                                    ParserDebug("P:constraints_decl:error yychar = %d\n", yychar);
436                                    if ( yychar == IDENTIFIER )
437                                    {
438                                        ParseError("Check previous line, Expected ',', got '%s'", yytext);
439                                    }
440                                    else
441                                    {
442                                        ParseError("Check previous line, Expected ';', got '%s'", yytext);
443                                    }
444                                    yyclearin;
445 
446                                 }
447 
448 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
449 
450 
451 constraints_decl:      constraints
452                        {
453                            /* Don't free these */
454                            strcpy(P.currentid,"");
455                            RlistDestroy(P.currentRlist);
456                            P.currentRlist = NULL;
457                            free(P.promiser);
458                            if (P.currentstring)
459                            {
460                                free(P.currentstring);
461                            }
462                            P.currentstring = NULL;
463                            P.promiser = NULL;
464                            P.promisee = NULL;
465                            /* reset argptrs etc*/
466                        }
467 
468 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
469 
470 constraints:           constraint                           /* BUNDLE ONLY */
471                      | constraints ',' constraint
472 
473 
474 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
475 
476 constraint:            constraint_id                        /* BUNDLE ONLY */
477                        assign_arrow
478                        rval
479                        {
480                            ParserHandleBundlePromiseRval();
481                        }
482 
483 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
484 
485 constraint_id:         IDENTIFIER                        /* BUNDLE ONLY */
486                        {
487                            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);
488 
489                            const PromiseTypeSyntax *promise_type_syntax = PromiseTypeSyntaxGet(P.blocktype, P.currenttype);
490                            if (promise_type_syntax == NULL)
491                            {
492                                // This promise type might be defined in another Policy object.
493                                // There is no way to distinguish a custom promise type
494                                // from a wrong (misspelled) promise type while parsing
495                                // since the Policy objects will be merged later.
496                            }
497                            else if (!PromiseTypeSyntaxGetConstraintSyntax(promise_type_syntax, P.currentid))
498                            {
499                                // Built in promise type with bad attribute
500                                ParseError("Unknown attribute '%s' for promise type '%s' in bundle with type '%s'", P.currentid, P.currenttype, P.blocktype);
501                                INSTALL_SKIP = true;
502                            }
503 
504                            strncpy(P.lval,P.currentid,CF_MAXVARSIZE);
505                            RlistDestroy(P.currentRlist);
506                            P.currentRlist = NULL;
507                        }
508                      | error
509                        {
510                              ParseError("Expected attribute, got '%s'\n", yytext);
511                        }
512 
513 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
514 
515 bodybody:              body_begin
516                        {
517                            ParserBeginBlockBody();
518                        }
519 
520                        bodybody_inner
521 
522                        '}'
523                        {
524                            ParserEndCurrentBlock();
525                        }
526 
527 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
528 
529 bodybody_inner:        /* empty */
530                      | bodyattribs
531 
532 bodyattribs:           bodyattrib                    /* BODY/PROMISE ONLY */
533                      | bodyattribs bodyattrib
534 
535 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
536 
537 bodyattrib:            class
538                      | selection_line
539 
540 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
541 
542 selection_line:        selection ';'
543                      | selection error
544                        {
545                           ParseError("Expected ';' check previous statement, got '%s'", yytext);
546                        }
547 
548 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
549 
550 selection:             selection_id                         /* BODY/PROMISE ONLY */
551                        assign_arrow
552                        rval
553                        {
554                            ParserHandleBlockAttributeRval();
555                        }
556 
557 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
558 
559 selection_id:          IDENTIFIER
560                        {
561                            ParserDebug("\tP:%s:%s:%s:%s attribute = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentid);
562 
563                            if (!INSTALL_SKIP)
564                            {
565                                const BodySyntax *body_syntax = BodySyntaxGet(P.block, P.currentbody->type);
566 
567                                if (!body_syntax || !BodySyntaxGetConstraintSyntax(body_syntax->constraints, P.currentid))
568                                {
569                                    ParseError(
570                                        "Unknown attribute '%s' for '%s %s %s'",
571                                        P.currentid,                // attribute name (lval)
572                                        ParserBlockString(P.block), // body     (block type)
573                                        P.currentbody->type,        // file     (body type)
574                                        P.blockid);                 // control  (body name)
575                                    INSTALL_SKIP = true;
576                                }
577 
578                                strncpy(P.lval,P.currentid,CF_MAXVARSIZE);
579                            }
580                            RlistDestroy(P.currentRlist);
581                            P.currentRlist = NULL;
582                        }
583                      | error
584                        {
585                           ParserDebug("P:selection_id:idsyntax:error yychar = %d\n", yychar);
586 
587                           if ( yychar == BUNDLE || yychar == BODY )
588                           {
589                              ParseError("Expected '}', got '%s'", yytext);
590                              /*
591                              YYABORT;
592                              */
593                           }
594                           else
595                           {
596                              ParseError("Expected attribute, got '%s'", yytext);
597                           }
598 
599                           yyclearin;
600                        }
601 
602 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
603 
604 assign_arrow:          FAT_ARROW
605                        {
606                            ParserDebug("\tP:=>\n");
607                        }
608                      | error
609                        {
610                           yyclearin;
611                           ParseError("Expected '=>', got '%s'", yytext);
612                        }
613 
614 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
615 
616 promisee_arrow:        THIN_ARROW
617                        {
618                            ParserDebug("\tP:->\n");
619                        }
620                        /* else we display the wrong error
621                      | error
622                        {
623                           yyclearin;
624                           ParseError("Expected '->', got '%s'", yytext);
625                        }
626                        */
627 
628 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
629 
630 class:                 CLASS_GUARD
631                        {
632                            P.offsets.last_class_id = P.offsets.current - strlen(P.currentclasses ? P.currentclasses : P.currentvarclasses) - 2;
633                            ParserDebug("\tP:%s:%s:%s:%s %s = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currenttype, P.currentclasses ? "class": "varclass", yytext);
634 
635                            if (P.currentclasses != NULL)
636                            {
637                                char *literal = xstrdup(P.currentclasses);
638 
639                                ValidateClassLiteral(literal);
640 
641                                free(literal);
642                            }
643                        }
644 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
645 
646 
647 
648 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
649 
650 rval:                  IDENTIFIER
651                        {
652                            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);
653                            RvalDestroy(P.rval);
654                            P.rval = (Rval) { xstrdup(P.currentid), RVAL_TYPE_SCALAR };
655                            P.references_body = true;
656                        }
657                      | QUOTED_STRING
658                        {
659                            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);
660                            RvalDestroy(P.rval);
661                            P.rval = (Rval) { P.currentstring, RVAL_TYPE_SCALAR };
662 
663                            P.currentstring = NULL;
664                            P.references_body = false;
665 
666                            if (P.currentpromise)
667                            {
668                                if (LvalWantsBody(P.currentpromise->parent_section->promise_type, P.lval))
669                                {
670                                    yyerror("An rvalue is quoted, but we expect an unquoted body identifier");
671                                }
672                            }
673                        }
674                      | NAKEDVAR
675                        {
676                            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);
677                            RvalDestroy(P.rval);
678                            P.rval = (Rval) { P.currentstring, RVAL_TYPE_SCALAR };
679 
680                            P.currentstring = NULL;
681                            P.references_body = false;
682                        }
683                      | list
684                        {
685                            ParserDebug("\tP:%s:%s:%s:%s install list =  %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.lval);
686                            RvalDestroy(P.rval);
687                            P.rval = (Rval) { RlistCopy(P.currentRlist), RVAL_TYPE_LIST };
688                            RlistDestroy(P.currentRlist);
689                            P.currentRlist = NULL;
690                            P.references_body = false;
691                        }
692                      | usefunction
693                        {
694                            RvalDestroy(P.rval);
695                            P.rval = (Rval) { P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL };
696                            P.currentfncall[P.arg_nesting+1] = NULL;
697                            P.references_body = false;
698                        }
699 
700                      | error
701                        {
702                            yyclearin;
703                            ParseError("Invalid r-value type '%s'", yytext);
704                        }
705 
706 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
707 
708 list:                  '{' '}'
709                      | '{' litems '}'
710                      | '{' litems ',' '}'
711 
712 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
713 
714 litems:
715                        litem
716                      | litems ',' litem
717                      | litem error
718                        {
719                            ParserDebug("P:rval:list:error yychar = %d\n", yychar);
720                            if ( yychar ==';' )
721                            {
722                                ParseError("Expected '}', wrong input '%s'", yytext);
723                            }
724                            else if ( yychar == FAT_ARROW )
725                            {
726                                ParseError("Check list statement previous line,"
727                                           " Expected '}', wrong input '%s'",
728                                           yytext);
729                            }
730                            else
731                            {
732                                ParseError("Expected ',', wrong input '%s'", yytext);
733                            }
734                            yyclearin;
735                        }
736 
737 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
738 
739 litem:                 IDENTIFIER
740                        {
741                            ParserDebug("\tP:%s:%s:%s:%s list append: "
742                                        "id = %s\n",
743                                        ParserBlockString(P.block), P.blocktype, P.blockid,
744                                        (P.currentclasses ?
745                                             P.currentclasses : "any"),
746                                        P.currentid);
747                            RlistAppendScalar((Rlist **) &P.currentRlist,
748                                              P.currentid);
749                        }
750 
751                      | QUOTED_STRING
752                        {
753                            ParserDebug("\tP:%s:%s:%s:%s list append: "
754                                        "qstring = %s\n",
755                                        ParserBlockString(P.block), P.blocktype, P.blockid,
756                                        (P.currentclasses ?
757                                             P.currentclasses : "any"),
758                                        P.currentstring);
759 
760                            ParserHandleQuotedListItem();
761                        }
762 
763                      | NAKEDVAR
764                        {
765                            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);
766                            RlistAppendScalar((Rlist **)&P.currentRlist,(void *)P.currentstring);
767                            free(P.currentstring);
768                            P.currentstring = NULL;
769                        }
770 
771                      | usefunction
772                        {
773                            RlistAppend(&P.currentRlist, P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL);
774                            FnCallDestroy(P.currentfncall[P.arg_nesting+1]);
775                            P.currentfncall[P.arg_nesting+1] = NULL;
776                        }
777 
778                      | error
779                        {
780                           yyclearin;
781                           ParseError("Invalid input for a list item, got '%s'", yytext);
782                        }
783 
784 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
785 
786 functionid:            IDENTIFIER
787                        {
788                            ParserDebug("\tP:%s:%s:%s:%s function id = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentid);
789                        }
790                      | NAKEDVAR
791                        {
792                            ParserDebug("\tP:%s:%s:%s:%s function nakedvar = %s\n", ParserBlockString(P.block), P.blocktype, P.blockid, P.currentclasses ? P.currentclasses : "any", P.currentstring);
793                            strncpy(P.currentid, P.currentstring, CF_MAXVARSIZE - 1); // Make a var look like an ID
794                            free(P.currentstring);
795                            P.currentstring = NULL;
796                        }
797 
798 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
799 
800 
801 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
802 
803 usefunction:           functionid givearglist
804                        {
805                            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);
806                        };
807 
808 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
809 
810 givearglist:           '('
811                        {
812                            if (++P.arg_nesting >= CF_MAX_NESTING)
813                            {
814                                fatal_yyerror("Nesting of functions is deeper than recommended");
815                            }
816                            P.currentfnid[P.arg_nesting] = xstrdup(P.currentid);
817                            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 );
818                        }
819 
820                        gaitems
821 
822                        ')'
823                        {
824                            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 );
825                            P.currentfncall[P.arg_nesting] = FnCallNew(P.currentfnid[P.arg_nesting], P.giveargs[P.arg_nesting]);
826                            P.giveargs[P.arg_nesting] = NULL;
827                            strcpy(P.currentid,"");
828                            free(P.currentfnid[P.arg_nesting]);
829                            P.currentfnid[P.arg_nesting] = NULL;
830                            P.arg_nesting--;
831                        }
832 
833 
834 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
835 
836 gaitems:               /* empty */
837                      | gaitem
838                      | gaitems ',' gaitem
839                      | gaitem error
840                        {
841                            ParseError("Expected ',', wrong input '%s'", yytext);
842                            yyclearin;
843                        }
844 
845 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
846 
847 gaitem:                IDENTIFIER
848                        {
849                            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);
850                            /* currently inside a use function */
851                            RlistAppendScalar(&P.giveargs[P.arg_nesting],P.currentid);
852                        }
853 
854                      | QUOTED_STRING
855                        {
856                            /* currently inside a use function */
857                            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);
858                            RlistAppendScalar(&P.giveargs[P.arg_nesting],P.currentstring);
859                            free(P.currentstring);
860                            P.currentstring = NULL;
861                        }
862 
863                      | NAKEDVAR
864                        {
865                            /* currently inside a use function */
866                            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);
867                            RlistAppendScalar(&P.giveargs[P.arg_nesting],P.currentstring);
868                            free(P.currentstring);
869                            P.currentstring = NULL;
870                        }
871 
872                      | usefunction
873                        {
874                            /* Careful about recursion */
875                            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);
876                            RlistAppend(&P.giveargs[P.arg_nesting], P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL);
877                            RvalDestroy((Rval) { P.currentfncall[P.arg_nesting+1], RVAL_TYPE_FNCALL });
878                            P.currentfncall[P.arg_nesting+1] = NULL;
879                        }
880 
881                      | error
882                        {
883                            ParserDebug("P:rval:function:gaitem:error yychar = %d\n", yychar);
884                            if (yychar == ';')
885                            {
886                               ParseError("Expected ')', wrong input '%s'", yytext);
887                            }
888                            else if (yychar == FAT_ARROW )
889                            {
890                               ParseError("Check function statement  previous line, Expected ')', wrong input '%s'", yytext);
891                            }
892                            else
893                            {
894                               ParseError("Invalid function argument, wrong input '%s'", yytext);
895                            }
896                            yyclearin;
897                        }
898 
899 %%
900