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.13 by root, Sun Oct 11 23:51:47 2009 UTC vs.
Revision 1.20 by root, Tue Oct 20 05:57:08 2009 UTC

66 condition in inv of originator 66 condition in inv of originator
67 67
68Once the final set of context objects has been established, each object 68Once the final set of context objects has been established, each object
69is matched against the C<condition>. 69is matched against the C<condition>.
70 70
71It is possible to chain modifiers from right-to-left, so this example
72would start with the originator, take it's inventory, find all inventory
73items which are potions, looks into their inventory, and then finds all
74spells.
75
76 type=SPELL in type=POTION in inv of originator
77
71Sometimes the server is only interested in knowing whether I<anything> 78Sometimes the server is only interested in knowing whether I<anything>
72matches, and sometimes the server is interested in I<all> objects that 79matches, and sometimes the server is interested in I<all> objects that
73match. 80match.
74 81
75=head2 OPERATORS 82=head2 OPERATORS
77=over 4 84=over 4
78 85
79=item and, or, not, () 86=item and, or, not, ()
80 87
81Conditions 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
82expressions. C<not> negates the expression, and parentheses can be used to 89expressions. C<not> negates the condition, and parentheses can be used to
83group 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)
84 102
85Example: match applied weapons. 103Example: match applied weapons.
86 104
87 applied type=WEAPON 105 applied type=WEAPON
88 106
110 128
111=item in env 129=item in env
112 130
113Replaces all objects by their containing object, if they have one. 131Replaces all objects by their containing object, if they have one.
114 132
133=item in arch
134
135Replaces all objects by their archetypes.
136
115=item in map 137=item in map
116 138
117Replaces 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.
118 140
119=item in <cond> 141=item in <condition>
120 142
121Finds all context objects matching the condition, and then puts their 143Finds all context objects matching the condition, and then puts their
122inventories into the context set. 144inventories into the context set.
123 145
124Note that C<in inv> is simply a special case of an C<< in <cond> >> that 146Note that C<in inv> is simply a special case of an C<< in <condition> >> that
125matches any object. 147matches any object.
126 148
127Example: find all spells inside potions inside the inventory of the context 149Example: find all spells inside potions inside the inventory of the context
128object(s). 150object(s).
129 151
136 158
137Example: check if the context object I<is> a spell, or I<contains> a spell. 159Example: check if the context object I<is> a spell, or I<contains> a spell.
138 160
139 type=SPELL also in inv 161 type=SPELL also in inv
140 162
141=item deep in ... 163=item also deep in ...
142 164
143Repeats the operation as many times as possible. This can be used to 165Repeats the operation as many times as possible. This can be used to
144recursively look into objects. 166recursively look into objects.
145 167
146=item also deep in ... 168So for example, C<also deep in inv> means to take the inventory of all
169objects, taking their inventories, and so on, and adding all these objects
170to the context set.
147 171
148C<also> and C<deep> can be combined. 172Similarly, C<also deep in env> means to take the environment object, their
173environemnt object and so on.
149 174
150Example: check if there are any unpaid items in an inventory, 175Example: check if there are any unpaid items in an inventory,
151or in the inventories of the inventory objects, and so on. 176or in the inventories of the inventory objects, and so on.
152 177
153 unpaid also deep in inv 178 unpaid also deep in inv
282 307
283Number of matching objects - the context object for the C<match> is the 308Number of matching objects - the context object for the C<match> is the
284currently tested object - you can override this with an C<in object> for 309currently tested object - you can override this with an C<in object> for
285example. 310example.
286 311
287=item match(match)
288
289An independent match - semantics like C<count>, except it only matters
290whether the match finds any object (which is faster).
291
292=item dump 312=item dump()
293 313
294Dumps the object to the server log when executed, and evaluates to true. 314Dumps the object to the server log when executed, and evaluates to true.
295 315
296Note that logical operations are short-circuiting, so this only dumps 316Note that logical operations are short-circuiting, so this only dumps
297potions: 317potions:
298 318
299 type=POTION and dump 319 type=POTION and dump()
300 320
301=back 321=back
302 322
303=head2 GRAMMAR 323=head2 GRAMMAR
304 324
313 root = 'object' | 'self' | 'source' | 'originator' 333 root = 'object' | 'self' | 'source' | 'originator'
314 chain = condition 334 chain = condition
315 | chain also deep 'in' set 335 | chain also deep 'in' set
316 also = nothing | 'also' 336 also = nothing | 'also'
317 deep = nothing | 'deep' 337 deep = nothing | 'deep'
318 set = 'inv' | 'env' | 'map' 338 set = 'inv' | 'env' | 'arch' | 'map'
319 339
320 empty = 340 empty =
321 341
322 # boolean matching condition 342 # boolean matching condition
323 343
324 condition = factor 344 condition = factor
325 | factor 'and'? cond 345 | factor 'and'? condition
326 | factor 'or' cond 346 | factor 'or' condition
327 347
328 factor = 'not' factor 348 factor = 'not' factor
329 | '(' cond ')' 349 | '(' match ')'
330 | expr 350 | expr
331 | expr operator constant 351 | expr operator constant
332 352
333 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 353 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
334 354
362 382
363use common::sense; 383use common::sense;
364 384
365use List::Util (); 385use List::Util ();
366 386
367# parser state
368# $_ # string to be parsed
369our $all; # find all, or just the first matching object
370
371{ 387{
372 package cf::match::exec; 388 package cf::match::exec;
373 389
374 use List::Util qw(first); 390 use List::Util qw(first);
375 391
379 395
380 sub ws { 396 sub ws {
381 /\G\s+/gc; 397 /\G\s+/gc;
382 } 398 }
383 399
400 sub condition ();
401 sub match ($$);
402
384 our %func = ( 403 our %func = (
385 has => sub { 404 has => sub {
386 'first { ' . &condition . ' } $_->inv' 405 'first { ' . condition . ' } $_->inv'
387 }, 406 },
388 count => sub { 407 count => sub {
389 local $all = 1;
390 '(scalar ' . &match ('$_') . ')' 408 '(scalar ' . (match 1, '$_') . ')'
391 },
392 match => sub {
393 local $all = 0;
394 '(scalar ' . &match ('$_') . ')'
395 },
396 );
397
398 our %special = (
399 any => sub {
400 1
401 }, 409 },
402 dump => sub { 410 dump => sub {
403 'do { 411 'do {
404 warn "cf::match::match dump:\n" 412 warn "cf::match::match dump:\n"
405 . "self: " . eval { $self->name } . "\n" 413 . "self: " . eval { $self->name } . "\n"
407 1 415 1
408 }'; 416 }';
409 }, 417 },
410 ); 418 );
411 419
420 our %special = (
421 any => sub {
422 1
423 },
424 );
425
412 sub constant { 426 sub constant {
413 ws; 427 ws;
414 428
415 return $1 if /\G([\-\+0-9\.]+)/gc; 429 return $1 if /\G([\-\+0-9\.]+)/gc;
416 return "cf::$1" if /\G([A-Z0-9_]+)/gc; 430 return "cf::$1" if /\G([A-Z0-9_]+)/gc;
471 } else { 485 } else {
472 $res .= constant; 486 $res .= constant;
473 } 487 }
474 488
475 } else { 489 } else {
490 Carp::cluck;#d#
476 die "expr expected\n"; 491 die "expr expected\n";
477 } 492 }
478 493
479 $res 494 $res
480 } 495 }
497 $res .= "!"; 512 $res .= "!";
498 } 513 }
499 514
500 if (/\G\(/gc) { 515 if (/\G\(/gc) {
501 # () 516 # ()
502 $res .= &condition; 517
503 ws; 518 $res .= '(' . (match 0, '$_') . ')';
519
504 /\G\)/gc or die "')' expected\n"; 520 /\G\s*\)/gc or die "closing ')' expected\n";
505 521
506 } else { 522 } else {
507 my $expr = expr; 523 my $expr = expr;
508 524
509 $res .= $expr; 525 $res .= $expr;
520 } 536 }
521 537
522 "($res)" 538 "($res)"
523 } 539 }
524 540
525 sub condition { 541 sub condition () {
526 my $res = factor; 542 my $res = factor;
527 543
528 while () { 544 while () {
529 ws; 545 ws;
530 546
531 # first check some stop-symbols, so we don't have to backtrack 547 # first check some stop-symbols, so we don't have to backtrack
532 if (/\G(?=also\b|deep\b|in\b|of\b\)|$)/gc) { 548 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
549 pos = pos; # argh. the misop hits again. again. again. again. you die.
533 last; 550 last;
534 551
535 } elsif (/\Gor\b/gc) { 552 } elsif (/\Gor\b/gc) {
536 $res .= " || "; 553 $res .= " || ";
537 554
543 } 560 }
544 561
545 $res 562 $res
546 } 563 }
547 564
548 sub match { 565 sub match ($$) {
549 my $default = shift; 566 my ($wantarray, $defctx) = @_;
550 567
568 my $res = condition;
569
570 # if nothing follows, we have a simple condition, so
571 # optimise a comon case.
572 if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) {
573 warn "shortcut<$res>$wantarray\n";#d#
574 return $wantarray
575 ? "$res ? \$_ : ()"
576 : $res;
577 }
578
551 my $res = ($all ? " grep { " : " first {") . condition . " }"; 579 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
552 580
553 while () { 581 while () {
554 ws; 582 ws;
555 583
556 my $also = /\Galso\s+/gc + 0; 584 my $also = /\Galso\s+/gc + 0;
557 my $deep = /\Gdeep\s+/gc + 0; 585 my $deep = /\Gdeep\s+/gc + 0;
558 586
559 if (/\Gin\s+/gc) { 587 if (/\Gin\s+/gc) {
560 my $expand; 588 my $expand;
561 589
562 if (/\G(inv|env|map)\b/gc) { 590 if (/\G(inv|env|map|arch)\b/gc) {
563 if ($1 eq "inv") { 591 if ($1 eq "inv") {
564 $expand = "map \$_->inv,"; 592 $expand = "map \$_->inv,";
565 } elsif ($1 eq "env") { 593 } elsif ($1 eq "env") {
566 $expand = "map \$_->env // (),"; 594 $expand = "map \$_->env // (),";
595 } elsif ($1 eq "arch") {
596 $expand = "map \$_->arch,";
597 $deep = 0; # infinite loop otherwise
567 } elsif ($1 eq "map") { 598 } elsif ($1 eq "map") {
568 $expand = "map \$_->map->at (\$_->x, \$_->y),"; 599 $expand = "map \$_->map->at (\$_->x, \$_->y),";
600 $deep = 0; # infinite loop otherwise
569 } 601 }
570 } else { 602 } else {
571 $expand = "map \$_->inv, grep { " . condition . " }"; 603 $expand = "map \$_->inv, grep { " . condition . " }";
572 } 604 }
573 605
581 $res .= " (\@res, \@_)\n" 613 $res .= " (\@res, \@_)\n"
582 . "}"; 614 . "}";
583 } else { 615 } else {
584 $res .= " $expand"; 616 $res .= " $expand";
585 } 617 }
618 } else {
619
586 } elsif (/\Gof\s+(self|object|source|originator)\b/gc) { 620 if (/\Gof\s+(self|object|source|originator)\b/gc) {
587 $also || $deep 621 $also || $deep
588 and die "neither 'also' nor 'deep' can be used with 'of'\n"; 622 and die "neither 'also' nor 'deep' can be used with 'of'\n";
589 623
590 if ($1 eq "self") { 624 if ($1 eq "self") {
591 return "$res \$self // ()"; 625 return "$res \$self // ()";
592 } elsif ($1 eq "object") { 626 } elsif ($1 eq "object") {
593 return "$res \$object"; 627 return "$res \$object";
594 } elsif ($1 eq "source") { 628 } elsif ($1 eq "source") {
595 return "$res \$source // ()"; 629 return "$res \$source // ()";
596 } elsif ($1 eq "originator") { 630 } elsif ($1 eq "originator") {
597 return "$res \$originator // \$source // ()"; 631 return "$res \$originator // \$source // ()";
632 }
633 } else {
634 return "$res $defctx";
598 } 635 }
599 } else {
600 return "$res $default";
601 } 636 }
602 } 637 }
603 } 638 }
604} 639}
605 640
606sub parse($;$) { 641sub parse($$) { # wantarray, matchexpr
607 local $_ = shift;
608 local $all = shift;
609
610 my $res; 642 my $res;
611 643
644 local $_ = $_[1];
645
612 eval { 646 eval {
613 $res = cf::match::parser::match "\$object"; 647 $res = cf::match::parser::match $_[0], "\$object";
614 648
615 /\G\s*$/gc 649 /\G$/gc
616 or die "unexpected trailing characters after match\n"; 650 or die "unexpected trailing characters after match\n";
617 }; 651 };
618 652
619 if ($@) { 653 if ($@) {
620 my $ctx = 20; 654 my $ctx = 20;
627 661
628 $res 662 $res
629} 663}
630 664
631if (0) {#d# 665if (0) {#d#
632 die parse 'applied', 1; 666 die parse 1, 'applied in inv';
633 exit 0; 667 exit 0;
634} 668}
635 669
636=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
637
638Compiles (and caches) the C<$match> expression and matches it against
639the C<$object>. C<$self> should be the object initiating the match (or
640C<undef>), C<$source> should be the actor/source and C<$originator> the
641object that initiated the action (such as the player). C<$originator>
642defaults to C<$source> when not given.
643
644In list context it finds and returns all matching objects, in scalar
645context only a true or false value.
646
647=cut
648
649our %CACHE; 670our %CACHE;
650 671
651sub compile($$) { 672sub compile($$) {
652 my ($match, $all) = @_; 673 my ($wantarray, $match) = @_;
653 my $expr = parse $match, $all; 674 my $expr = parse $wantarray, $match;
654 warn "MATCH DEBUG $match,$all => $expr\n";#d# 675 warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
655 $expr = eval " 676 $expr = eval "
656 package cf::match::exec; 677 package cf::match::exec;
657 sub { 678 sub {
658 my (\$object, \$self, \$source, \$originator) = \@_; 679 my (\$object, \$self, \$source, \$originator) = \@_;
659 $expr 680 $expr
662 die if $@; 683 die if $@;
663 684
664 $expr 685 $expr
665} 686}
666 687
688=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
689
690Compiles (and caches) the C<$match> expression and matches it against
691the C<$object>. C<$self> should be the object initiating the match (or
692C<undef>), C<$source> should be the actor/source and C<$originator> the
693object that initiated the action (such as the player). C<$originator>
694defaults to C<$source> when not given.
695
696In list context it finds and returns all matching objects, in scalar
697context only a true or false value.
698
699=cut
700
667sub match($$;$$$) { 701sub match($$;$$$) {
668 my $match = shift; 702 my $match = shift;
669 my $all = wantarray+0; 703 my $wantarray = wantarray+0;
670 704
671 &{ 705 &{
672 $CACHE{"$all$match"} ||= compile $match, $all 706 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
673 } 707 }
674} 708}
675 709
710our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
711 %CACHE = ();
712};
713
676#d# $::schmorp=cf::player::find "schmorp"& 714#d# $::schmorp=cf::player::find "schmorp"&
677#d# cf::match::match '', $::schmorp->ob 715#d# cf::match::match '', $::schmorp->ob
678 716
679 717
680=back 718=back

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines