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