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