ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf/match.pm
(Generate patch)

Comparing deliantra/server/lib/cf/match.pm (file contents):
Revision 1.19 by root, Tue Oct 13 15:58:19 2009 UTC vs.
Revision 1.22 by root, Sat Oct 24 11:45:40 2009 UTC

84=over 4 84=over 4
85 85
86=item and, or, not, () 86=item and, or, not, ()
87 87
88Conditions can be combined with C<and> or C<or> to build larger 88Conditions can be combined with C<and> or C<or> to build larger
89expressions. C<not> negates the expression, and parentheses can be used to 89expressions. C<not> negates the condition, and parentheses can be used to
90group conditions. 90override operator precedence and execute submatches.
91
92Not that C<not> only negates a condition and not the whole match
93expressions, thus
94
95 not applied in inv
96
97is true if there is I<any> non-object in the inventory. To negate a whole
98match, you have to use a sub-match. To check whether there is I<no>
99applied object in someones inventory, write this:
100
101 not (applied in inv)
91 102
92Example: match applied weapons. 103Example: match applied weapons.
93 104
94 applied type=WEAPON 105 applied type=WEAPON
95 106
125 136
126=item in map 137=item in map
127 138
128Replaces all objects by the objects that are on the same mapspace as them. 139Replaces all objects by the objects that are on the same mapspace as them.
129 140
141=item in head
142
143Replaces all objects by their head objects.
144
130=item in <condition> 145=item in <condition>
131 146
132Finds all context objects matching the condition, and then puts their 147Finds all context objects matching the condition, and then puts their
133inventories into the context set. 148inventories into the context set.
134 149
286=item any 301=item any
287 302
288This simply evaluates to true, and simply makes matching I<any> object a 303This simply evaluates to true, and simply makes matching I<any> object a
289bit easier to read. 304bit easier to read.
290 305
306=item none
307
308This simply evaluates to false, and simply makes matching I<never> a bit
309easier to read.
310
291=item has(condition) 311=item has(condition)
292 312
293True iff the object has a matching inventory object. 313True iff the object has a matching inventory object.
294 314
295=item count(match) 315=item count(match)
296 316
297Number of matching objects - the context object for the C<match> is the 317Number of matching objects - the context object for the C<match> is the
298currently tested object - you can override this with an C<in object> for 318currently tested object - you can override this with an C<in object> for
299example. 319example.
300
301=item match(match)
302
303An independent match - semantics like C<count>, except it only matters
304whether the match finds any object (which is faster).
305 320
306=item dump() 321=item dump()
307 322
308Dumps the object to the server log when executed, and evaluates to true. 323Dumps the object to the server log when executed, and evaluates to true.
309 324
324 339
325 match = chain 340 match = chain
326 | chain 'of' root 341 | chain 'of' root
327 root = 'object' | 'self' | 'source' | 'originator' 342 root = 'object' | 'self' | 'source' | 'originator'
328 chain = condition 343 chain = condition
329 | chain also deep 'in' set 344 | chain also deep 'in' modifier
330 also = nothing | 'also' 345 also = nothing | 'also'
331 deep = nothing | 'deep' 346 deep = nothing | 'deep'
332 set = 'inv' | 'env' | 'arch' | 'map' 347 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
333 348
334 empty = 349 nothing =
335 350
336 # boolean matching condition 351 # boolean matching condition
337 352
338 condition = factor 353 condition = factor
339 | factor 'and'? condition 354 | factor 'and'? condition
340 | factor 'or' condition 355 | factor 'or' condition
341 356
342 factor = 'not' factor 357 factor = 'not' factor
343 | '(' condition ')' 358 | '(' match ')'
344 | expr 359 | expr
345 | expr operator constant 360 | expr operator constant
346 361
347 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 362 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
348 363
349 expr = flag 364 expr = flag
350 | sattr 365 | sattr
351 | aattr '[' <constant> ']' 366 | aattr '[' <constant> ']'
367 | 'stat.' statattr
352 | special 368 | special
353 | func '(' args ')' 369 | func '(' args ')'
354 | '{' perl code block '}' 370 | '{' perl code block '}'
355 371
356 func = <any function name> 372 func = <any function name>
357 sattr = <any scalar object attribute> 373 sattr = <any scalar object attribute>
358 aattr = <any array object attribute> 374 aattr = <any array object attribute>
359 flag = <any object flag> 375 flag = <any object flag>
376 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
360 special = <any ()-less "function"> 377 special = <any ()-less "function">
361 378
362 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 379 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
363 args = <depends on function> 380 args = <depends on function>
364 381
375package cf::match; 392package cf::match;
376 393
377use common::sense; 394use common::sense;
378 395
379use List::Util (); 396use List::Util ();
380
381# parser state
382# $_ # string to be parsed
383our $all; # find all, or just the first matching object
384 397
385{ 398{
386 package cf::match::exec; 399 package cf::match::exec;
387 400
388 use List::Util qw(first); 401 use List::Util qw(first);
393 406
394 sub ws { 407 sub ws {
395 /\G\s+/gc; 408 /\G\s+/gc;
396 } 409 }
397 410
411 sub condition ();
412 sub match ($$);
413
398 our %func = ( 414 our %func = (
399 has => sub { 415 has => sub {
400 'first { ' . &condition . ' } $_->inv' 416 'first { ' . condition . ' } $_->inv'
401 }, 417 },
402 count => sub { 418 count => sub {
403 local $all = 1;
404 '(scalar ' . &match ('$_') . ')' 419 '(scalar ' . (match 1, '$_') . ')'
405 },
406 match => sub {
407 local $all = 0;
408 '(scalar ' . &match ('$_') . ')'
409 }, 420 },
410 dump => sub { 421 dump => sub {
411 'do { 422 'do {
412 warn "cf::match::match dump:\n" 423 warn "cf::match::match dump:\n"
413 . "self: " . eval { $self->name } . "\n" 424 . "self: " . eval { $self->name } . "\n"
419 430
420 our %special = ( 431 our %special = (
421 any => sub { 432 any => sub {
422 1 433 1
423 }, 434 },
435 none => sub {
436 0
437 },
424 ); 438 );
425 439
426 sub constant { 440 sub constant {
427 ws; 441 ws;
428 442
436 } 450 }
437 451
438 our $flag = $cf::REFLECT{object}{flags}; 452 our $flag = $cf::REFLECT{object}{flags};
439 our $sattr = $cf::REFLECT{object}{scalars}; 453 our $sattr = $cf::REFLECT{object}{scalars};
440 our $aattr = $cf::REFLECT{object}{arrays}; 454 our $aattr = $cf::REFLECT{object}{arrays};
455 our $lattr = $cf::REFLECT{living}{scalars};
441 456
442 sub expr { 457 sub expr {
443 # ws done by factor 458 # ws done by factor
444 my $res; 459 my $res;
445 460
448 463
449 my $expr = $1; 464 my $expr = $1;
450 465
451 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 466 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
452 467
468 } elsif (/\Gstats\.([A-Za-z0-9_]+)/gc) {
469
470 if (exists $lattr->{$1}) {
471 $res .= "\$_->stats->$1";
472 } elsif (exists $lattr->{"\u$1"}) {
473 $res .= "\$_->stats->\u$1";
474 } else {
475 die "living statistic name expected (str, pow, hp, sp...)\n";
476 }
477
453 } elsif (/\G([A-Za-z0-9_]+)/gc) { 478 } elsif (/\G([A-Za-z0-9_]+)/gc) {
454 479
455 if (my $func = $func{$1}) { 480 if (my $func = $func{$1}) {
456 /\G\s*\(/gc 481 /\G\s*\(/gc
457 or die "'(' expected after function name\n"; 482 or die "'(' expected after function name\n";
485 } else { 510 } else {
486 $res .= constant; 511 $res .= constant;
487 } 512 }
488 513
489 } else { 514 } else {
515 Carp::cluck;#d#
490 die "expr expected\n"; 516 die "expr expected\n";
491 } 517 }
492 518
493 $res 519 $res
494 } 520 }
511 $res .= "!"; 537 $res .= "!";
512 } 538 }
513 539
514 if (/\G\(/gc) { 540 if (/\G\(/gc) {
515 # () 541 # ()
516 $res .= &condition; 542
517 ws; 543 $res .= '(' . (match 0, '$_') . ')';
544
518 /\G\)/gc or die "')' expected\n"; 545 /\G\s*\)/gc or die "closing ')' expected\n";
519 546
520 } else { 547 } else {
521 my $expr = expr; 548 my $expr = expr;
522 549
523 $res .= $expr; 550 $res .= $expr;
534 } 561 }
535 562
536 "($res)" 563 "($res)"
537 } 564 }
538 565
539 sub condition { 566 sub condition () {
540 my $res = factor; 567 my $res = factor;
541 568
542 while () { 569 while () {
543 ws; 570 ws;
544 571
545 # first check some stop-symbols, so we don't have to backtrack 572 # first check some stop-symbols, so we don't have to backtrack
546 if (/\G(?=also\b|deep\b|in\b|of\b\)|\z)/gc) { 573 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
547 pos = pos; # argh. the misop hits again. again. again. again. you die. 574 pos = pos; # argh. the misop hits again. again. again. again. you die.
548 last; 575 last;
549 576
550 } elsif (/\Gor\b/gc) { 577 } elsif (/\Gor\b/gc) {
551 $res .= " || "; 578 $res .= " || ";
558 } 585 }
559 586
560 $res 587 $res
561 } 588 }
562 589
563 sub match { 590 sub match ($$) {
564 my $default = shift; 591 my ($wantarray, $defctx) = @_;
565 592
593 my $res = condition;
594
595 # if nothing follows, we have a simple condition, so
596 # optimise a comon case.
597 if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) {
598 return $wantarray
599 ? "$res ? \$_ : ()"
600 : $res;
601 }
602
566 my $res = ($all ? " grep { " : " first {") . condition . " }"; 603 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
567 604
568 while () { 605 while () {
569 ws; 606 ws;
570 607
571 my $also = /\Galso\s+/gc + 0; 608 my $also = /\Galso\s+/gc + 0;
572 my $deep = /\Gdeep\s+/gc + 0; 609 my $deep = /\Gdeep\s+/gc + 0;
573 610
574 if (/\Gin\s+/gc) { 611 if (/\Gin\s+/gc) {
575 my $expand; 612 my $expand;
576 613
577 if (/\G(inv|env|map|arch)\b/gc) { 614 if (/\G(inv|env|map|arch|head)\b/gc) {
578 if ($1 eq "inv") { 615 if ($1 eq "inv") {
579 $expand = "map \$_->inv,"; 616 $expand = "map \$_->inv,";
580 } elsif ($1 eq "env") { 617 } elsif ($1 eq "env") {
581 $expand = "map \$_->env // (),"; 618 $expand = "map \$_->env // (),";
619 } elsif ($1 eq "head") {
620 $expand = "map \$_->head,";
621 $deep = 0; # infinite loop otherwise
582 } elsif ($1 eq "arch") { 622 } elsif ($1 eq "arch") {
583 $expand = "map \$_->arch,"; 623 $expand = "map \$_->arch,";
584 $deep = 0; # infinite loop otherwise 624 $deep = 0; # infinite loop otherwise
585 } elsif ($1 eq "map") { 625 } elsif ($1 eq "map") {
586 $expand = "map \$_->map->at (\$_->x, \$_->y),"; 626 $expand = "map \$_->map->at (\$_->x, \$_->y),";
600 $res .= " (\@res, \@_)\n" 640 $res .= " (\@res, \@_)\n"
601 . "}"; 641 . "}";
602 } else { 642 } else {
603 $res .= " $expand"; 643 $res .= " $expand";
604 } 644 }
645 } else {
646
605 } elsif (/\Gof\s+(self|object|source|originator)\b/gc) { 647 if (/\Gof\s+(self|object|source|originator)\b/gc) {
606 $also || $deep 648 $also || $deep
607 and die "neither 'also' nor 'deep' can be used with 'of'\n"; 649 and die "neither 'also' nor 'deep' can be used with 'of'\n";
608 650
609 if ($1 eq "self") { 651 if ($1 eq "self") {
610 return "$res \$self // ()"; 652 return "$res \$self // ()";
611 } elsif ($1 eq "object") { 653 } elsif ($1 eq "object") {
612 return "$res \$object"; 654 return "$res \$object";
613 } elsif ($1 eq "source") { 655 } elsif ($1 eq "source") {
614 return "$res \$source // ()"; 656 return "$res \$source // ()";
615 } elsif ($1 eq "originator") { 657 } elsif ($1 eq "originator") {
616 return "$res \$originator // \$source // ()"; 658 return "$res \$originator // \$source // ()";
659 }
660 } else {
661 return "$res $defctx";
617 } 662 }
618 } else {
619 return "$res $default";
620 } 663 }
621 } 664 }
622 } 665 }
623} 666}
624 667
625sub parse($;$) { 668sub parse($$) { # wantarray, matchexpr
626 local $_ = shift;
627 local $all = shift;
628
629 my $res; 669 my $res;
630 670
671 local $_ = $_[1];
672
631 eval { 673 eval {
632 $res = cf::match::parser::match "\$object"; 674 $res = cf::match::parser::match $_[0], "\$object";
633 675
634 /\G$/gc 676 /\G$/gc
635 or die "unexpected trailing characters after match\n"; 677 or die "unexpected trailing characters after match\n";
636 }; 678 };
637 679
646 688
647 $res 689 $res
648} 690}
649 691
650if (0) {#d# 692if (0) {#d#
651 die parse 'type=SPELL_EFFECT and match(name="bullet" in arch)', 1; 693 die parse 1, 'stats.pow';
652 exit 0; 694 exit 0;
653} 695}
654 696
655=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
656
657Compiles (and caches) the C<$match> expression and matches it against
658the C<$object>. C<$self> should be the object initiating the match (or
659C<undef>), C<$source> should be the actor/source and C<$originator> the
660object that initiated the action (such as the player). C<$originator>
661defaults to C<$source> when not given.
662
663In list context it finds and returns all matching objects, in scalar
664context only a true or false value.
665
666=cut
667
668our %CACHE; 697our %CACHE;
669 698
670sub compile($$) { 699sub compile($$) {
671 my ($match, $all) = @_; 700 my ($wantarray, $match) = @_;
672 my $expr = parse $match, $all; 701 my $expr = parse $wantarray, $match;
673 warn "MATCH DEBUG $match,$all => $expr\n";#d# 702 warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
674 $expr = eval " 703 $expr = eval "
675 package cf::match::exec; 704 package cf::match::exec;
676 sub { 705 sub {
677 my (\$object, \$self, \$source, \$originator) = \@_; 706 my (\$object, \$self, \$source, \$originator) = \@_;
678 $expr 707 $expr
681 die if $@; 710 die if $@;
682 711
683 $expr 712 $expr
684} 713}
685 714
715=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
716
717Compiles (and caches) the C<$match> expression and matches it against
718the C<$object>. C<$self> should be the object initiating the match (or
719C<undef>), C<$source> should be the actor/source and C<$originator> the
720object that initiated the action (such as the player). C<$originator>
721defaults to C<$source> when not given.
722
723In list context it finds and returns all matching objects, in scalar
724context only a true or false value.
725
726=cut
727
686sub match($$;$$$) { 728sub match($$;$$$) {
687 my $match = shift; 729 my $match = shift;
688 my $all = wantarray+0; 730 my $wantarray = wantarray+0;
689 731
690 &{ 732 &{
691 $CACHE{"$all$match"} ||= compile $match, $all 733 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
692 } 734 }
693} 735}
694 736
695our $CACHE_CLEARER = AE::timer 3600, 3600, sub { 737our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
696 %CACHE = (); 738 %CACHE = ();

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines