1/* 2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. 3 * 4 * This file is part of Jam - see jam.c for Copyright information. 5 */ 6 7/* 8 * compile.c - compile parsed jam statements 9 * 10 * External routines: 11 * 12 * compile_append() - append list results of two statements 13 * compile_break() - compile 'break/continue/return' rule 14 * compile_eval() - evaluate if to determine which leg to compile 15 * compile_foreach() - compile the "for x in y" statement 16 * compile_if() - compile 'if' rule 17 * compile_include() - support for 'include' - call include() on file 18 * compile_list() - expand and return a list 19 * compile_local() - declare (and set) local variables 20 * compile_null() - do nothing -- a stub for parsing 21 * compile_on() - run rule under influence of on-target variables 22 * compile_rule() - compile a single user defined rule 23 * compile_rules() - compile a chain of rules 24 * compile_set() - compile the "set variable" statement 25 * compile_setcomp() - support for `rule` - save parse tree 26 * compile_setexec() - support for `actions` - save execution string 27 * compile_settings() - compile the "on =" (set variable on exec) statement 28 * compile_switch() - compile 'switch' rule 29 * 30 * Internal routines: 31 * 32 * debug_compile() - printf with indent to show rule expansion. 33 * evaluate_rule() - execute a rule invocation 34 * 35 * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of 36 * the awkward sounding "settings". 37 * 04/12/94 (seiwald) - Combined build_depends() with build_includes(). 38 * 04/12/94 (seiwald) - actionlist() now just appends a single action. 39 * 04/13/94 (seiwald) - added shorthand L0 for null list pointer 40 * 05/13/94 (seiwald) - include files are now bound as targets, and thus 41 * can make use of $(SEARCH) 42 * 06/01/94 (seiwald) - new 'actions existing' does existing sources 43 * 08/23/94 (seiwald) - Support for '+=' (append to variable) 44 * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. 45 * 01/22/95 (seiwald) - Exit rule. 46 * 02/02/95 (seiwald) - Always rule; LEAVES rule. 47 * 02/14/95 (seiwald) - NoUpdate rule. 48 * 01/20/00 (seiwald) - Upgraded from K&R to ANSI C 49 * 09/07/00 (seiwald) - stop crashing when a rule redefines itself 50 * 09/11/00 (seiwald) - new evaluate_rule() for headers(). 51 * 09/11/00 (seiwald) - rules now return values, accessed via [ rule arg ... ] 52 * 09/12/00 (seiwald) - don't complain about rules invoked without targets 53 * 01/13/01 (seiwald) - fix case where rule is defined within another 54 * 01/10/01 (seiwald) - built-ins split out to builtin.c. 55 * 01/11/01 (seiwald) - optimize compile_rules() for tail recursion 56 * 01/21/01 (seiwald) - replace evaluate_if() with compile_eval() 57 * 01/24/01 (seiwald) - 'while' statement 58 * 03/23/01 (seiwald) - "[ on target rule ]" support 59 * 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx 60 * 03/02/02 (seiwald) - rules can be invoked via variable names 61 * 03/12/02 (seiwald) - &&,&,||,|,in now short-circuit again 62 * 03/25/02 (seiwald) - if ( "" a b ) one again returns true 63 * 06/21/02 (seiwald) - support for named parameters 64 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr() 65 * 10/22/02 (seiwald) - working return/break/continue statements 66 * 11/04/02 (seiwald) - const-ing for string literals 67 * 11/18/02 (seiwald) - remove bogus search() in 'on' statement. 68 * 12/17/02 (seiwald) - new copysettings() to protect target-specific vars 69 */ 70 71# ifdef OPT_RULE_PROFILING_EXT 72# include <sys/time.h> 73# endif 74 75# include "jam.h" 76 77# include "lists.h" 78# include "parse.h" 79# include "compile.h" 80# include "variable.h" 81# include "expand.h" 82# include "rules.h" 83# include "newstr.h" 84# include "search.h" 85 86static const char *set_names[] = { "=", "+=", "?=" }; 87static void debug_compile( int which, const char *s ); 88int glob( const char *s, const char *c ); 89 90 91 92/* 93 * compile_append() - append list results of two statements 94 * 95 * parse->left more compile_append() by left-recursion 96 * parse->right single rule 97 */ 98 99LIST * 100compile_append( 101 PARSE *parse, 102 LOL *args, 103 int *jmp ) 104{ 105 /* Append right to left. */ 106 107 return list_append( 108 (*parse->left->func)( parse->left, args, jmp ), 109 (*parse->right->func)( parse->right, args, jmp ) ); 110} 111 112/* 113 * compile_break() - compile 'break/continue/return' rule 114 * 115 * parse->left results 116 * parse->num JMP_BREAK/CONTINUE/RETURN 117 */ 118 119LIST * 120compile_break( 121 PARSE *parse, 122 LOL *args, 123 int *jmp ) 124{ 125 LIST *lv = (*parse->left->func)( parse->left, args, jmp ); 126 *jmp = parse->num; 127 return lv; 128} 129 130/* 131 * compile_eval() - evaluate if to determine which leg to compile 132 * 133 * Returns: 134 * list if expression true - compile 'then' clause 135 * L0 if expression false - compile 'else' clause 136 */ 137 138static int 139lcmp( LIST *t, LIST *s ) 140{ 141 int status = 0; 142 143 while( !status && ( t || s ) ) 144 { 145 const char *st = t ? t->string : ""; 146 const char *ss = s ? s->string : ""; 147 148 status = strcmp( st, ss ); 149 150 t = t ? list_next( t ) : t; 151 s = s ? list_next( s ) : s; 152 } 153 154 return status; 155} 156 157LIST * 158compile_eval( 159 PARSE *parse, 160 LOL *args, 161 int *jmp ) 162{ 163 LIST *ll, *lr, *s, *t; 164 int status = 0; 165 166 /* Short circuit lr eval for &&, ||, and 'in' */ 167 168 ll = (*parse->left->func)( parse->left, args, jmp ); 169 lr = 0; 170 171 switch( parse->num ) 172 { 173 case EXPR_AND: 174 case EXPR_IN: if( ll ) goto eval; break; 175 case EXPR_OR: if( !ll ) goto eval; break; 176 default: eval: lr = (*parse->right->func)( parse->right, args, jmp ); 177 } 178 179 /* Now eval */ 180 181 switch( parse->num ) 182 { 183 case EXPR_NOT: 184 if( !ll ) status = 1; 185 break; 186 187 case EXPR_AND: 188 if( ll && lr ) status = 1; 189 break; 190 191 case EXPR_OR: 192 if( ll || lr ) status = 1; 193 break; 194 195 case EXPR_IN: 196 /* "a in b": make sure each of */ 197 /* ll is equal to something in lr. */ 198 199 for( t = ll; t; t = list_next( t ) ) 200 { 201 for( s = lr; s; s = list_next( s ) ) 202 if( !strcmp( t->string, s->string ) ) 203 break; 204 if( !s ) break; 205 } 206 207 /* No more ll? Success */ 208 209 if( !t ) status = 1; 210 211 break; 212 213 case EXPR_EXISTS: if( lcmp( ll, L0 ) != 0 ) status = 1; break; 214 case EXPR_EQUALS: if( lcmp( ll, lr ) == 0 ) status = 1; break; 215 case EXPR_NOTEQ: if( lcmp( ll, lr ) != 0 ) status = 1; break; 216 case EXPR_LESS: if( lcmp( ll, lr ) < 0 ) status = 1; break; 217 case EXPR_LESSEQ: if( lcmp( ll, lr ) <= 0 ) status = 1; break; 218 case EXPR_MORE: if( lcmp( ll, lr ) > 0 ) status = 1; break; 219 case EXPR_MOREEQ: if( lcmp( ll, lr ) >= 0 ) status = 1; break; 220 221 } 222 223 if( DEBUG_IF ) 224 { 225 debug_compile( 0, "if" ); 226 list_print( ll ); 227 printf( "(%d) ", status ); 228 list_print( lr ); 229 printf( "\n" ); 230 } 231 232 /* Find something to return. */ 233 /* In odd circumstances (like "" = "") */ 234 /* we'll have to return a new string. */ 235 236 if( !status ) t = 0; 237 else if( ll ) t = ll, ll = 0; 238 else if( lr ) t = lr, lr = 0; 239 else t = list_new( L0, "1", 0 ); 240 241 if( ll ) list_free( ll ); 242 if( lr ) list_free( lr ); 243 return t; 244} 245 246/* 247 * compile_foreach() - compile the "for x in y" statement 248 * 249 * Compile_foreach() resets the given variable name to each specified 250 * value, executing the commands enclosed in braces for each iteration. 251 * 252 * parse->string index variable 253 * parse->left variable values 254 * parse->right rule to compile 255 */ 256 257LIST * 258compile_foreach( 259 PARSE *p, 260 LOL *args, 261 int *jmp ) 262{ 263 LIST *nv = (*p->left->func)( p->left, args, jmp ); 264 LIST *result = 0; 265 LIST *l; 266 267 /* for each value for var */ 268 269 for( l = nv; l && *jmp == JMP_NONE; l = list_next( l ) ) 270 { 271 /* Reset $(p->string) for each val. */ 272 273 var_set( p->string, list_new( L0, l->string, 1 ), VAR_SET ); 274 275 /* Keep only last result. */ 276 277 list_free( result ); 278 result = (*p->right->func)( p->right, args, jmp ); 279 280 /* continue loop? */ 281 282 if( *jmp == JMP_CONTINUE ) 283 *jmp = JMP_NONE; 284 } 285 286 /* Here by break/continue? */ 287 288 if( *jmp == JMP_BREAK || *jmp == JMP_CONTINUE ) 289 *jmp = JMP_NONE; 290 291 list_free( nv ); 292 293 /* Returns result of last loop */ 294 295 return result; 296} 297 298/* 299 * compile_if() - compile 'if' rule 300 * 301 * parse->left condition tree 302 * parse->right then tree 303 * parse->third else tree 304 */ 305 306LIST * 307compile_if( 308 PARSE *p, 309 LOL *args, 310 int *jmp ) 311{ 312 LIST *l = (*p->left->func)( p->left, args, jmp ); 313 314 p = l ? p->right : p->third; 315 316 list_free( l ); 317 318 return (*p->func)( p, args, jmp ); 319} 320 321/* 322 * compile_include() - support for 'include' - call include() on file 323 * 324 * parse->left list of files to include (can only do 1) 325 */ 326 327LIST * 328compile_include( 329 PARSE *parse, 330 LOL *args, 331 int *jmp ) 332{ 333 LIST *nt = (*parse->left->func)( parse->left, args, jmp ); 334 335 if( DEBUG_COMPILE ) 336 { 337 debug_compile( 0, "include" ); 338 list_print( nt ); 339 printf( "\n" ); 340 } 341 342 if( nt ) 343 { 344 TARGET *t = bindtarget( nt->string ); 345 346 /* Bind the include file under the influence of */ 347 /* "on-target" variables. Though they are targets, */ 348 /* include files are not built with make(). */ 349 /* Needn't copysettings(), as search sets no vars. */ 350 351 pushsettings( t->settings ); 352 t->boundname = search( t->name, &t->time ); 353 popsettings( t->settings ); 354 355 /* Don't parse missing file if NOCARE set */ 356 357 if( t->time || !( t->flags & T_FLAG_NOCARE ) ) 358 parse_file( t->boundname ); 359 } 360 361 list_free( nt ); 362 363 return L0; 364} 365 366/* 367 * compile_list() - expand and return a list 368 * 369 * parse->string - character string to expand 370 */ 371 372LIST * 373compile_list( 374 PARSE *parse, 375 LOL *args, 376 int *jmp ) 377{ 378 /* voodoo 1 means: s is a copyable string */ 379 const char *s = parse->string; 380 return var_expand( L0, s, s + strlen( s ), args, 1 ); 381} 382 383/* 384 * compile_local() - declare (and set) local variables 385 * 386 * parse->left list of variables 387 * parse->right list of values 388 * parse->third rules to execute 389 */ 390 391LIST * 392compile_local( 393 PARSE *parse, 394 LOL *args, 395 int *jmp ) 396{ 397 LIST *l; 398 SETTINGS *s = 0; 399 LIST *nt = (*parse->left->func)( parse->left, args, jmp ); 400 LIST *ns = (*parse->right->func)( parse->right, args, jmp ); 401 LIST *result; 402 403 if( DEBUG_COMPILE ) 404 { 405 debug_compile( 0, "local" ); 406 list_print( nt ); 407 printf( " = " ); 408 list_print( ns ); 409 printf( "\n" ); 410 } 411 412 /* Initial value is ns */ 413 414 for( l = nt; l; l = list_next( l ) ) 415 s = addsettings( s, 0, l->string, list_copy( (LIST*)0, ns ) ); 416 417 list_free( ns ); 418 list_free( nt ); 419 420 /* Note that callees of the current context get this "local" */ 421 /* variable, making it not so much local as layered. */ 422 423 pushsettings( s ); 424 result = (*parse->third->func)( parse->third, args, jmp ); 425 popsettings( s ); 426 freesettings( s ); 427 428 return result; 429} 430 431/* 432 * compile_null() - do nothing -- a stub for parsing 433 */ 434 435LIST * 436compile_null( 437 PARSE *parse, 438 LOL *args, 439 int *jmp ) 440{ 441 return L0; 442} 443 444/* 445 * compile_on() - run rule under influence of on-target variables 446 * 447 * parse->left target list; only first used 448 * parse->right rule to run 449 */ 450 451LIST * 452compile_on( 453 PARSE *parse, 454 LOL *args, 455 int *jmp ) 456{ 457 LIST *nt = (*parse->left->func)( parse->left, args, jmp ); 458 LIST *result = 0; 459 460 if( DEBUG_COMPILE ) 461 { 462 debug_compile( 0, "on" ); 463 list_print( nt ); 464 printf( "\n" ); 465 } 466 467 /* 468 * Copy settings, so that 'on target var on target = val' 469 * doesn't set var globally. 470 */ 471 472 if( nt ) 473 { 474 TARGET *t = bindtarget( nt->string ); 475 SETTINGS *s = copysettings( t->settings ); 476 477 pushsettings( s ); 478 result = (*parse->right->func)( parse->right, args, jmp ); 479 popsettings( s ); 480 freesettings( s ); 481 } 482 483 list_free( nt ); 484 485 return result; 486} 487 488/* 489 * compile_rule() - compile a single user defined rule 490 * 491 * parse->left list of rules to run 492 * parse->right parameters (list of lists) to rule, recursing left 493 * 494 * Wrapped around evaluate_rule() so that headers() can share it. 495 */ 496 497LIST * 498compile_rule( 499 PARSE *parse, 500 LOL *args, 501 int *jmp ) 502{ 503 LOL nargs[1]; 504 LIST *result = 0; 505 LIST *ll, *l; 506 PARSE *p; 507 508 /* list of rules to run -- normally 1! */ 509 510 ll = (*parse->left->func)( parse->left, args, jmp ); 511 512 /* Build up the list of arg lists */ 513 514 lol_init( nargs ); 515 516 for( p = parse->right; p; p = p->left ) 517 lol_add( nargs, (*p->right->func)( p->right, args, jmp ) ); 518 519 /* Run rules, appending results from each */ 520 521 for( l = ll; l; l = list_next( l ) ) 522 { 523 int localJmp = JMP_NONE; 524 result = evaluate_rule( l->string, nargs, result, &localJmp ); 525 if (localJmp == JMP_EOF) 526 { 527 *jmp = JMP_EOF; 528 break; 529 } 530 } 531 532 list_free( ll ); 533 lol_free( nargs ); 534 535 return result; 536} 537 538/* 539 * evaluate_rule() - execute a rule invocation 540 */ 541 542LIST * 543evaluate_rule( 544 const char *rulename, 545 LOL *args, 546 LIST *result, 547 int *jmp ) 548{ 549 RULE *rule = bindrule( rulename ); 550 551 if( DEBUG_COMPILE ) 552 { 553 debug_compile( 1, rulename ); 554 lol_print( args ); 555 printf( "\n" ); 556 } 557 558 /* Check traditional targets $(<) and sources $(>) */ 559 560 if( !rule->actions && !rule->procedure ) 561 printf( "warning: unknown rule %s\n", rule->name ); 562 563 /* If this rule will be executed for updating the targets */ 564 /* then construct the action for make(). */ 565 566 if( rule->actions ) 567 { 568 TARGETS *t; 569 ACTION *action; 570 571 /* The action is associated with this instance of this rule */ 572 573 action = (ACTION *)malloc( sizeof( ACTION ) ); 574 memset( (char *)action, '\0', sizeof( *action ) ); 575 576 action->rule = rule; 577 action->targets = targetlist( (TARGETS *)0, lol_get( args, 0 ) ); 578 action->sources = targetlist( (TARGETS *)0, lol_get( args, 1 ) ); 579 580 /* Make targets[1,N-1] depend on targets[0], to describe the */ 581 /* generated targets for the rule. Do it with includes, to */ 582 /* reflect non-build dependency. */ 583 584 if( action->targets ) 585 { 586 TARGET *t0 = action->targets->target; 587 for( t = action->targets->next; t; t = t->next ) 588 { 589 TARGET *tn = t->target; 590 if( !tn->includes ) 591 { 592 tn->includes = copytarget( tn ); 593 } 594 tn = tn->includes; 595 tn->depends = targetentry( tn->depends, t0 ); 596 } 597 } 598 599 /* Append this action to the actions of each target */ 600 601 for( t = action->targets; t; t = t->next ) 602 t->target->actions = actionlist( t->target->actions, action ); 603 } 604 605 /* Now recursively compile any parse tree associated with this rule */ 606 607 if( rule->procedure ) 608 { 609 PARSE *parse = rule->procedure; 610 SETTINGS *s = 0; 611 LIST *l; 612 int i; 613 614# ifdef OPT_RULE_PROFILING_EXT 615 struct timeval startTime, endTime; 616 617 if ( DEBUG_PROFILE_RULES ) 618 gettimeofday(&startTime, 0); 619# endif 620 621 /* build parameters as local vars */ 622 623 for( l = rule->params, i = 0; l; l = l->next, i++ ) 624 s = addsettings( s, 0, l->string, 625 list_copy( L0, lol_get( args, i ) ) ); 626 627 /* Run rule. */ 628 /* Bring in local params. */ 629 /* refer/free to ensure rule not freed during use. */ 630 631 parse_refer( parse ); 632 633 pushsettings( s ); 634 result = list_append( result, (*parse->func)( parse, args, jmp ) ); 635 popsettings( s ); 636 freesettings( s ); 637 638 parse_free( parse ); 639 640# ifdef OPT_RULE_PROFILING_EXT 641 if ( DEBUG_PROFILE_RULES ) 642 { 643 gettimeofday(&endTime, 0); 644 645 rule->invocations++; 646 rule->invocation_time 647 += (endTime.tv_sec - startTime.tv_sec) * (int64_t)1000000 648 + (endTime.tv_usec - startTime.tv_usec); 649 } 650# endif 651 652 } 653 654 if( DEBUG_COMPILE ) 655 debug_compile( -1, 0 ); 656 657 return result; 658} 659 660/* 661 * compile_rules() - compile a chain of rules 662 * 663 * parse->left single rule 664 * parse->right more compile_rules() by right-recursion 665 */ 666 667LIST * 668compile_rules( 669 PARSE *parse, 670 LOL *args, 671 int *jmp ) 672{ 673 /* Ignore result from first statement; return the 2nd. */ 674 /* Optimize recursion on the right by looping. */ 675 676 LIST *result = 0; 677 678 while( *jmp == JMP_NONE && parse->func == compile_rules ) 679 { 680 list_free( result ); 681 result = (*parse->left->func)( parse->left, args, jmp ); 682 parse = parse->right; 683 } 684 685 if( *jmp == JMP_NONE ) 686 { 687 list_free( result ); 688 result = (*parse->func)( parse, args, jmp ); 689 } 690 691 return result; 692} 693 694/* 695 * compile_set() - compile the "set variable" statement 696 * 697 * parse->left variable names 698 * parse->right variable values 699 * parse->num VAR_SET/APPEND/DEFAULT 700 */ 701 702LIST * 703compile_set( 704 PARSE *parse, 705 LOL *args, 706 int *jmp ) 707{ 708 LIST *nt = (*parse->left->func)( parse->left, args, jmp ); 709 LIST *ns = (*parse->right->func)( parse->right, args, jmp ); 710 LIST *l; 711 712 if( DEBUG_COMPILE ) 713 { 714 debug_compile( 0, "set" ); 715 list_print( nt ); 716 printf( " %s ", set_names[ parse->num ] ); 717 list_print( ns ); 718 printf( "\n" ); 719 } 720 721 /* Call var_set to set variable */ 722 /* var_set keeps ns, so need to copy it */ 723 724 for( l = nt; l; l = list_next( l ) ) 725 var_set( l->string, list_copy( L0, ns ), parse->num ); 726 727 list_free( nt ); 728 729 return ns; 730} 731 732/* 733 * compile_setcomp() - support for `rule` - save parse tree 734 * 735 * parse->string rule name 736 * parse->left list of argument names 737 * parse->right rules for rule 738 */ 739 740LIST * 741compile_setcomp( 742 PARSE *parse, 743 LOL *args, 744 int *jmp ) 745{ 746 RULE *rule = bindrule( parse->string ); 747 LIST *params = 0; 748 PARSE *p; 749 750 /* Build param list */ 751 752 for( p = parse->left; p; p = p->left ) 753 params = list_new( params, p->string, 1 ); 754 755 if( DEBUG_COMPILE ) 756 { 757 debug_compile( 0, "rule" ); 758 printf( "%s ", parse->string ); 759 list_print( params ); 760 printf( "\n" ); 761 } 762 763 /* Free old one, if present */ 764 765 if( rule->procedure ) 766 parse_free( rule->procedure ); 767 768 if( rule->params ) 769 list_free( rule->params ); 770 771 rule->procedure = parse->right; 772 rule->params = params; 773 774 /* we now own this parse tree */ 775 /* don't let parse_free() release it */ 776 777 parse_refer( parse->right ); 778 779 return L0; 780} 781 782/* 783 * compile_setexec() - support for `actions` - save execution string 784 * 785 * parse->string rule name 786 * parse->string1 OS command string 787 * parse->num flags 788 * parse->left `bind` variables 789 * 790 * Note that the parse flags (as defined in compile.h) are transfered 791 * directly to the rule flags (as defined in rules.h). 792 */ 793 794LIST * 795compile_setexec( 796 PARSE *parse, 797 LOL *args, 798 int *jmp ) 799{ 800 RULE *rule = bindrule( parse->string ); 801 LIST *bindlist = (*parse->left->func)( parse->left, args, jmp ); 802 803 /* Free old one, if present */ 804 805 if( rule->actions ) 806 { 807 freestr( rule->actions ); 808 list_free( rule->bindlist ); 809 } 810 811 rule->actions = copystr( parse->string1 ); 812 rule->bindlist = bindlist; 813 rule->flags = parse->num; 814 815 return L0; 816} 817 818/* 819 * compile_settings() - compile the "on =" (set variable on exec) statement 820 * 821 * parse->left variable names 822 * parse->right target name 823 * parse->third variable value 824 * parse->num VAR_SET/APPEND/DEFAULT 825 */ 826 827LIST * 828compile_settings( 829 PARSE *parse, 830 LOL *args, 831 int *jmp ) 832{ 833 LIST *nt = (*parse->left->func)( parse->left, args, jmp ); 834 LIST *ns = (*parse->third->func)( parse->third, args, jmp ); 835 LIST *targets = (*parse->right->func)( parse->right, args, jmp ); 836 LIST *ts; 837 838 if( DEBUG_COMPILE ) 839 { 840 debug_compile( 0, "set" ); 841 list_print( nt ); 842 printf( "on " ); 843 list_print( targets ); 844 printf( " %s ", set_names[ parse->num ] ); 845 list_print( ns ); 846 printf( "\n" ); 847 } 848 849 /* Call addsettings to save variable setting */ 850 /* addsettings keeps ns, so need to copy it */ 851 /* Pass append flag to addsettings() */ 852 853 for( ts = targets; ts; ts = list_next( ts ) ) 854 { 855 TARGET *t = bindtarget( ts->string ); 856 LIST *l; 857 858 for( l = nt; l; l = list_next( l ) ) 859 t->settings = addsettings( t->settings, parse->num, 860 l->string, list_copy( (LIST*)0, ns ) ); 861 } 862 863 list_free( nt ); 864 list_free( targets ); 865 866 return ns; 867} 868 869/* 870 * compile_switch() - compile 'switch' rule 871 * 872 * parse->left switch value (only 1st used) 873 * parse->right cases 874 * 875 * cases->left 1st case 876 * cases->right next cases 877 * 878 * case->string argument to match 879 * case->left parse tree to execute 880 */ 881 882LIST * 883compile_switch( 884 PARSE *parse, 885 LOL *args, 886 int *jmp ) 887{ 888 LIST *nt = (*parse->left->func)( parse->left, args, jmp ); 889 LIST *result = 0; 890 891 if( DEBUG_COMPILE ) 892 { 893 debug_compile( 0, "switch" ); 894 list_print( nt ); 895 printf( "\n" ); 896 } 897 898 /* Step through cases */ 899 900 for( parse = parse->right; parse; parse = parse->right ) 901 { 902 if( !glob( parse->left->string, nt ? nt->string : "" ) ) 903 { 904 /* Get & exec parse tree for this case */ 905 parse = parse->left->left; 906 result = (*parse->func)( parse, args, jmp ); 907 break; 908 } 909 } 910 911 list_free( nt ); 912 913 return result; 914} 915 916/* 917 * compile_while() - compile 'while' rule 918 * 919 * parse->left condition tree 920 * parse->right execution tree 921 */ 922 923LIST * 924compile_while( 925 PARSE *p, 926 LOL *args, 927 int *jmp ) 928{ 929 LIST *result = 0; 930 LIST *l; 931 932 /* Returns the value from the last execution of the block */ 933 934 while( ( *jmp == JMP_NONE ) && 935 ( l = (*p->left->func)( p->left, args, jmp ) ) ) 936 { 937 /* Always toss while's expression */ 938 939 list_free( l ); 940 941 /* Keep only last result. */ 942 943 list_free( result ); 944 result = (*p->right->func)( p->right, args, jmp ); 945 946 /* continue loop? */ 947 948 if( *jmp == JMP_CONTINUE ) 949 *jmp = JMP_NONE; 950 } 951 952 /* Here by break/continue? */ 953 954 if( *jmp == JMP_BREAK || *jmp == JMP_CONTINUE ) 955 *jmp = JMP_NONE; 956 957 /* Returns result of last loop */ 958 959 return result; 960} 961 962/* 963 * debug_compile() - printf with indent to show rule expansion. 964 */ 965 966static void 967debug_compile( int which, const char *s ) 968{ 969 static int level = 0; 970 static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|"; 971 int i = ((1+level) * 2) % 35; 972 973 if( which >= 0 ) 974 printf( "%*.*s ", i, i, indent ); 975 976 if( s ) 977 printf( "%s ", s ); 978 979 level += which; 980} 981