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