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.22 by root, Sat Oct 24 11:45:40 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
141=item in head
142
143Replaces all objects by their head objects.
144
119=item in <cond> 145=item in <condition>
120 146
121Finds all context objects matching the condition, and then puts their 147Finds all context objects matching the condition, and then puts their
122inventories into the context set. 148inventories into the context set.
123 149
124Note that C<in inv> is simply a special case of an C<< in <cond> >> that 150Note that C<in inv> is simply a special case of an C<< in <condition> >> that
125matches any object. 151matches any object.
126 152
127Example: find all spells inside potions inside the inventory of the context 153Example: find all spells inside potions inside the inventory of the context
128object(s). 154object(s).
129 155
136 162
137Example: check if the context object I<is> a spell, or I<contains> a spell. 163Example: check if the context object I<is> a spell, or I<contains> a spell.
138 164
139 type=SPELL also in inv 165 type=SPELL also in inv
140 166
141=item deep in ... 167=item also deep in ...
142 168
143Repeats the operation as many times as possible. This can be used to 169Repeats the operation as many times as possible. This can be used to
144recursively look into objects. 170recursively look into objects.
145 171
146=item also deep in ... 172So for example, C<also deep in inv> means to take the inventory of all
173objects, taking their inventories, and so on, and adding all these objects
174to the context set.
147 175
148C<also> and C<deep> can be combined. 176Similarly, C<also deep in env> means to take the environment object, their
177environemnt object and so on.
149 178
150Example: check if there are any unpaid items in an inventory, 179Example: check if there are any unpaid items in an inventory,
151or in the inventories of the inventory objects, and so on. 180or in the inventories of the inventory objects, and so on.
152 181
153 unpaid also deep in inv 182 unpaid also deep in inv
272=item any 301=item any
273 302
274This 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
275bit easier to read. 304bit easier to read.
276 305
306=item none
307
308This simply evaluates to false, and simply makes matching I<never> a bit
309easier to read.
310
277=item has(condition) 311=item has(condition)
278 312
279True iff the object has a matching inventory object. 313True iff the object has a matching inventory object.
280 314
281=item count(match) 315=item count(match)
282 316
283Number 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
284currently 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
285example. 319example.
286 320
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 321=item dump()
293 322
294Dumps 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.
295 324
296Note that logical operations are short-circuiting, so this only dumps 325Note that logical operations are short-circuiting, so this only dumps
297potions: 326potions:
298 327
299 type=POTION and dump 328 type=POTION and dump()
300 329
301=back 330=back
302 331
303=head2 GRAMMAR 332=head2 GRAMMAR
304 333
310 339
311 match = chain 340 match = chain
312 | chain 'of' root 341 | chain 'of' root
313 root = 'object' | 'self' | 'source' | 'originator' 342 root = 'object' | 'self' | 'source' | 'originator'
314 chain = condition 343 chain = condition
315 | chain also deep 'in' set 344 | chain also deep 'in' modifier
316 also = nothing | 'also' 345 also = nothing | 'also'
317 deep = nothing | 'deep' 346 deep = nothing | 'deep'
318 set = 'inv' | 'env' | 'map' 347 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
319 348
320 empty = 349 nothing =
321 350
322 # boolean matching condition 351 # boolean matching condition
323 352
324 condition = factor 353 condition = factor
325 | factor 'and'? cond 354 | factor 'and'? condition
326 | factor 'or' cond 355 | factor 'or' condition
327 356
328 factor = 'not' factor 357 factor = 'not' factor
329 | '(' cond ')' 358 | '(' match ')'
330 | expr 359 | expr
331 | expr operator constant 360 | expr operator constant
332 361
333 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 362 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
334 363
335 expr = flag 364 expr = flag
336 | sattr 365 | sattr
337 | aattr '[' <constant> ']' 366 | aattr '[' <constant> ']'
367 | 'stat.' statattr
338 | special 368 | special
339 | func '(' args ')' 369 | func '(' args ')'
340 | '{' perl code block '}' 370 | '{' perl code block '}'
341 371
342 func = <any function name> 372 func = <any function name>
343 sattr = <any scalar object attribute> 373 sattr = <any scalar object attribute>
344 aattr = <any array object attribute> 374 aattr = <any array object attribute>
345 flag = <any object flag> 375 flag = <any object flag>
376 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
346 special = <any ()-less "function"> 377 special = <any ()-less "function">
347 378
348 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 379 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
349 args = <depends on function> 380 args = <depends on function>
350 381
361package cf::match; 392package cf::match;
362 393
363use common::sense; 394use common::sense;
364 395
365use List::Util (); 396use List::Util ();
366
367# parser state
368# $_ # string to be parsed
369our $all; # find all, or just the first matching object
370 397
371{ 398{
372 package cf::match::exec; 399 package cf::match::exec;
373 400
374 use List::Util qw(first); 401 use List::Util qw(first);
379 406
380 sub ws { 407 sub ws {
381 /\G\s+/gc; 408 /\G\s+/gc;
382 } 409 }
383 410
411 sub condition ();
412 sub match ($$);
413
384 our %func = ( 414 our %func = (
385 has => sub { 415 has => sub {
386 'first { ' . &condition . ' } $_->inv' 416 'first { ' . condition . ' } $_->inv'
387 }, 417 },
388 count => sub { 418 count => sub {
389 local $all = 1;
390 '(scalar ' . &match ('$_') . ')' 419 '(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 }, 420 },
402 dump => sub { 421 dump => sub {
403 'do { 422 'do {
404 warn "cf::match::match dump:\n" 423 warn "cf::match::match dump:\n"
405 . "self: " . eval { $self->name } . "\n" 424 . "self: " . eval { $self->name } . "\n"
407 1 426 1
408 }'; 427 }';
409 }, 428 },
410 ); 429 );
411 430
431 our %special = (
432 any => sub {
433 1
434 },
435 none => sub {
436 0
437 },
438 );
439
412 sub constant { 440 sub constant {
413 ws; 441 ws;
414 442
415 return $1 if /\G([\-\+0-9\.]+)/gc; 443 return $1 if /\G([\-\+0-9\.]+)/gc;
416 return "cf::$1" if /\G([A-Z0-9_]+)/gc; 444 return "cf::$1" if /\G([A-Z0-9_]+)/gc;
422 } 450 }
423 451
424 our $flag = $cf::REFLECT{object}{flags}; 452 our $flag = $cf::REFLECT{object}{flags};
425 our $sattr = $cf::REFLECT{object}{scalars}; 453 our $sattr = $cf::REFLECT{object}{scalars};
426 our $aattr = $cf::REFLECT{object}{arrays}; 454 our $aattr = $cf::REFLECT{object}{arrays};
455 our $lattr = $cf::REFLECT{living}{scalars};
427 456
428 sub expr { 457 sub expr {
429 # ws done by factor 458 # ws done by factor
430 my $res; 459 my $res;
431 460
434 463
435 my $expr = $1; 464 my $expr = $1;
436 465
437 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 466 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
438 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
439 } elsif (/\G([A-Za-z0-9_]+)/gc) { 478 } elsif (/\G([A-Za-z0-9_]+)/gc) {
440 479
441 if (my $func = $func{$1}) { 480 if (my $func = $func{$1}) {
442 /\G\s*\(/gc 481 /\G\s*\(/gc
443 or die "'(' expected after function name\n"; 482 or die "'(' expected after function name\n";
471 } else { 510 } else {
472 $res .= constant; 511 $res .= constant;
473 } 512 }
474 513
475 } else { 514 } else {
515 Carp::cluck;#d#
476 die "expr expected\n"; 516 die "expr expected\n";
477 } 517 }
478 518
479 $res 519 $res
480 } 520 }
497 $res .= "!"; 537 $res .= "!";
498 } 538 }
499 539
500 if (/\G\(/gc) { 540 if (/\G\(/gc) {
501 # () 541 # ()
502 $res .= &condition; 542
503 ws; 543 $res .= '(' . (match 0, '$_') . ')';
544
504 /\G\)/gc or die "')' expected\n"; 545 /\G\s*\)/gc or die "closing ')' expected\n";
505 546
506 } else { 547 } else {
507 my $expr = expr; 548 my $expr = expr;
508 549
509 $res .= $expr; 550 $res .= $expr;
520 } 561 }
521 562
522 "($res)" 563 "($res)"
523 } 564 }
524 565
525 sub condition { 566 sub condition () {
526 my $res = factor; 567 my $res = factor;
527 568
528 while () { 569 while () {
529 ws; 570 ws;
530 571
531 # 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
532 if (/\G(?=also\b|deep\b|in\b|of\b\)|$)/gc) { 573 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
574 pos = pos; # argh. the misop hits again. again. again. again. you die.
533 last; 575 last;
534 576
535 } elsif (/\Gor\b/gc) { 577 } elsif (/\Gor\b/gc) {
536 $res .= " || "; 578 $res .= " || ";
537 579
543 } 585 }
544 586
545 $res 587 $res
546 } 588 }
547 589
548 sub match { 590 sub match ($$) {
549 my $default = shift; 591 my ($wantarray, $defctx) = @_;
550 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
551 my $res = ($all ? " grep { " : " first {") . condition . " }"; 603 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
552 604
553 while () { 605 while () {
554 ws; 606 ws;
555 607
556 my $also = /\Galso\s+/gc + 0; 608 my $also = /\Galso\s+/gc + 0;
557 my $deep = /\Gdeep\s+/gc + 0; 609 my $deep = /\Gdeep\s+/gc + 0;
558 610
559 if (/\Gin\s+/gc) { 611 if (/\Gin\s+/gc) {
560 my $expand; 612 my $expand;
561 613
562 if (/\G(inv|env|map)\b/gc) { 614 if (/\G(inv|env|map|arch|head)\b/gc) {
563 if ($1 eq "inv") { 615 if ($1 eq "inv") {
564 $expand = "map \$_->inv,"; 616 $expand = "map \$_->inv,";
565 } elsif ($1 eq "env") { 617 } elsif ($1 eq "env") {
566 $expand = "map \$_->env // (),"; 618 $expand = "map \$_->env // (),";
619 } elsif ($1 eq "head") {
620 $expand = "map \$_->head,";
621 $deep = 0; # infinite loop otherwise
622 } elsif ($1 eq "arch") {
623 $expand = "map \$_->arch,";
624 $deep = 0; # infinite loop otherwise
567 } elsif ($1 eq "map") { 625 } elsif ($1 eq "map") {
568 $expand = "map \$_->map->at (\$_->x, \$_->y),"; 626 $expand = "map \$_->map->at (\$_->x, \$_->y),";
627 $deep = 0; # infinite loop otherwise
569 } 628 }
570 } else { 629 } else {
571 $expand = "map \$_->inv, grep { " . condition . " }"; 630 $expand = "map \$_->inv, grep { " . condition . " }";
572 } 631 }
573 632
581 $res .= " (\@res, \@_)\n" 640 $res .= " (\@res, \@_)\n"
582 . "}"; 641 . "}";
583 } else { 642 } else {
584 $res .= " $expand"; 643 $res .= " $expand";
585 } 644 }
645 } else {
646
586 } elsif (/\Gof\s+(self|object|source|originator)\b/gc) { 647 if (/\Gof\s+(self|object|source|originator)\b/gc) {
587 $also || $deep 648 $also || $deep
588 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";
589 650
590 if ($1 eq "self") { 651 if ($1 eq "self") {
591 return "$res \$self // ()"; 652 return "$res \$self // ()";
592 } elsif ($1 eq "object") { 653 } elsif ($1 eq "object") {
593 return "$res \$object"; 654 return "$res \$object";
594 } elsif ($1 eq "source") { 655 } elsif ($1 eq "source") {
595 return "$res \$source // ()"; 656 return "$res \$source // ()";
596 } elsif ($1 eq "originator") { 657 } elsif ($1 eq "originator") {
597 return "$res \$originator // \$source // ()"; 658 return "$res \$originator // \$source // ()";
659 }
660 } else {
661 return "$res $defctx";
598 } 662 }
599 } else {
600 return "$res $default";
601 } 663 }
602 } 664 }
603 } 665 }
604} 666}
605 667
606sub parse($;$) { 668sub parse($$) { # wantarray, matchexpr
607 local $_ = shift;
608 local $all = shift;
609
610 my $res; 669 my $res;
611 670
671 local $_ = $_[1];
672
612 eval { 673 eval {
613 $res = cf::match::parser::match "\$object"; 674 $res = cf::match::parser::match $_[0], "\$object";
614 675
615 /\G\s*$/gc 676 /\G$/gc
616 or die "unexpected trailing characters after match\n"; 677 or die "unexpected trailing characters after match\n";
617 }; 678 };
618 679
619 if ($@) { 680 if ($@) {
620 my $ctx = 20; 681 my $ctx = 20;
627 688
628 $res 689 $res
629} 690}
630 691
631if (0) {#d# 692if (0) {#d#
632 die parse 'applied', 1; 693 die parse 1, 'stats.pow';
633 exit 0; 694 exit 0;
634} 695}
635 696
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; 697our %CACHE;
650 698
651sub compile($$) { 699sub compile($$) {
652 my ($match, $all) = @_; 700 my ($wantarray, $match) = @_;
653 my $expr = parse $match, $all; 701 my $expr = parse $wantarray, $match;
654 warn "MATCH DEBUG $match,$all => $expr\n";#d# 702 warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
655 $expr = eval " 703 $expr = eval "
656 package cf::match::exec; 704 package cf::match::exec;
657 sub { 705 sub {
658 my (\$object, \$self, \$source, \$originator) = \@_; 706 my (\$object, \$self, \$source, \$originator) = \@_;
659 $expr 707 $expr
662 die if $@; 710 die if $@;
663 711
664 $expr 712 $expr
665} 713}
666 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
667sub match($$;$$$) { 728sub match($$;$$$) {
668 my $match = shift; 729 my $match = shift;
669 my $all = wantarray+0; 730 my $wantarray = wantarray+0;
670 731
671 &{ 732 &{
672 $CACHE{"$all$match"} ||= compile $match, $all 733 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
673 } 734 }
674} 735}
675 736
737our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
738 %CACHE = ();
739};
740
676#d# $::schmorp=cf::player::find "schmorp"& 741#d# $::schmorp=cf::player::find "schmorp"&
677#d# cf::match::match '', $::schmorp->ob 742#d# cf::match::match '', $::schmorp->ob
678 743
679 744
680=back 745=back

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines