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