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.15 by root, Mon Oct 12 17:37:43 2009 UTC vs.
Revision 1.35 by root, Tue Jan 3 11:25:33 2012 UTC

1#
2# This file is part of Deliantra, the Roguelike Realtime MMORPG.
3#
4# Copyright (©) 2009,2010,2011,2012 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5#
6# Deliantra is free software: you can redistribute it and/or modify it under
7# the terms of the Affero GNU General Public License as published by the
8# Free Software Foundation, either version 3 of the License, or (at your
9# option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the Affero GNU General Public License
17# and the GNU General Public License along with this program. If not, see
18# <http://www.gnu.org/licenses/>.
19#
20# The authors can be reached via e-mail to <support@deliantra.net>
21#
22
1=head1 NAME 23=head1 NAME
2 24
3cf::match - object matching language 25cf::match - object matching language
4 26
5=head1 DESCRIPTION 27=head1 DESCRIPTION
66 condition in inv of originator 88 condition in inv of originator
67 89
68Once the final set of context objects has been established, each object 90Once the final set of context objects has been established, each object
69is matched against the C<condition>. 91is matched against the C<condition>.
70 92
93It is possible to chain modifiers from right-to-left, so this example
94would start with the originator, take it's inventory, find all inventory
95items which are potions, looks into their inventory, and then finds all
96spells.
97
98 type=SPELL in type=POTION in inv of originator
99
71Sometimes the server is only interested in knowing whether I<anything> 100Sometimes the server is only interested in knowing whether I<anything>
72matches, and sometimes the server is interested in I<all> objects that 101matches, and sometimes the server is interested in I<all> objects that
73match. 102match.
74 103
75=head2 OPERATORS 104=head2 OPERATORS
77=over 4 106=over 4
78 107
79=item and, or, not, () 108=item and, or, not, ()
80 109
81Conditions can be combined with C<and> or C<or> to build larger 110Conditions can be combined with C<and> or C<or> to build larger
82expressions. C<not> negates the expression, and parentheses can be used to 111expressions. C<not> negates the condition, and parentheses can be used to
83group conditions. 112override operator precedence and execute submatches.
113
114Not that C<not> only negates a condition and not the whole match
115expressions, thus
116
117 not applied in inv
118
119is true if there is I<any> non-applied object in the inventory. To negate
120a whole match, you have to use a sub-match: To check whether there is
121I<no> applied object in someones inventory, write this:
122
123 not (applied in inv)
84 124
85Example: match applied weapons. 125Example: match applied weapons.
86 126
87 applied type=WEAPON 127 applied type=WEAPON
88 128
89Example: match horns or rods. 129Example: match horns or rods.
90 130
91 type=HORN or type=ROD 131 type=HORN or type=ROD
132
133Example: see if the originator is a player.
134
135 type=PLAYER of originator
92 136
93=item in ... 137=item in ...
94 138
95The in operator takes the context set and modifies it in various ways. As 139The in operator takes the context set and modifies it in various ways. As
96a less technical description, think of the C<in> as being a I<look into> 140a less technical description, think of the C<in> as being a I<look into>
118 162
119=item in map 163=item in map
120 164
121Replaces all objects by the objects that are on the same mapspace as them. 165Replaces all objects by the objects that are on the same mapspace as them.
122 166
167=item in head
168
169Replaces all objects by their head objects.
170
123=item in <condition> 171=item in <condition>
124 172
125Finds all context objects matching the condition, and then puts their 173Finds all context objects matching the condition, and then puts their
126inventories into the context set. 174inventories into the context set.
127 175
140 188
141Example: check if the context object I<is> a spell, or I<contains> a spell. 189Example: check if the context object I<is> a spell, or I<contains> a spell.
142 190
143 type=SPELL also in inv 191 type=SPELL also in inv
144 192
145=item deep in ... 193=item also deep in ...
146 194
147Repeats the operation as many times as possible. This can be used to 195Repeats the operation as many times as possible. This can be used to
148recursively look into objects. 196recursively look into objects.
149 197
150=item also deep in ... 198So for example, C<also deep in inv> means to take the inventory of all
199objects, taking their inventories, and so on, and adding all these objects
200to the context set.
151 201
152C<also> and C<deep> can be combined. 202Similarly, C<also deep in env> means to take the environment object, their
203environemnt object and so on.
153 204
154Example: check if there are any unpaid items in an inventory, 205Example: check if there are any unpaid items in an inventory,
155or in the inventories of the inventory objects, and so on. 206or in the inventories of the inventory objects, and so on.
156 207
157 unpaid also deep in inv 208 unpaid also deep in inv
178Starts with the default object - this is the object passed to the match to 229Starts with the default object - this is the object passed to the match to
179match against by default. Matches have an explicit C<of object> appended, 230match against by default. Matches have an explicit C<of object> appended,
180but submatches start at the current object, and in this case C<of object> 231but submatches start at the current object, and in this case C<of object>
181can be used to start at the original object once more. 232can be used to start at the original object once more.
182 233
234=item of self
235
236Starts with the object initiating/asking for the match - this is basically
237always the object that the match expression is attached to.
238
183=item of source 239=item of source
184 240
185Starts with the I<source> object - this object is sometimes passed to 241Starts with the I<source> object - this object is sometimes passed to
186matches and represents the object that is the source of the action, such 242matches and represents the object that is the source of the action, such
187as a rod or a potion when it is applied. Often, the I<source> is the same 243as a rod or a potion when it is applied. Often, the I<source> is the same
194the original initiator of an action, most commonly a player or monster. 250the original initiator of an action, most commonly a player or monster.
195 251
196This object is often identical to the I<source> (e.g. when a player casts 252This object is often identical to the I<source> (e.g. when a player casts
197a spell, the player is both source and originator). 253a spell, the player is both source and originator).
198 254
199=item of self
200
201Starts with the object initiating/asking for the match - this is basically
202always the object that the match expression is attached to.
203
204=back 255=back
205 256
206=head2 EXPRESSIONS 257=head2 EXPRESSIONS
207 258
208Expressions used in conditions usually consist of simple boolean checks 259Expressions used in conditions usually consist of simple boolean checks
217 268
218=item scalar object attributes 269=item scalar object attributes
219 270
220Object attributes that consist of a single value (C<name>, C<title>, 271Object attributes that consist of a single value (C<name>, C<title>,
221C<value> and so on) can be specified by simply using their name, in which 272C<value> and so on) can be specified by simply using their name, in which
222acse their corresponding value is used. 273case their corresponding value is used.
223 274
224=item array objects attributes 275=item array objects attributes
225 276
226The C<resist> array can be accessed by specifying C<< resist [ ATNR_type ] 277The C<resist> array can be accessed by specifying C<< resist [ ATNR_type ]
227>>. 278>>.
231 resist[ATNR_ACID] > 30 282 resist[ATNR_ACID] > 30
232 283
233=item functions 284=item functions
234 285
235Some additional functions with or without arguments in parentheses are 286Some additional functions with or without arguments in parentheses are
236available. 287available. They are documented in their own section, below.
237 288
238=item { BLOCK } 289=item { BLOCK }
239 290
240You can specify perl code to execute by putting it inside curly 291You can specify perl code to execute by putting it inside curly
241braces. The last expression evaluated inside will become the result. 292braces. The last expression evaluated inside will become the result.
276=item any 327=item any
277 328
278This simply evaluates to true, and simply makes matching I<any> object a 329This simply evaluates to true, and simply makes matching I<any> object a
279bit easier to read. 330bit easier to read.
280 331
332=item none
333
334This simply evaluates to false, and simply makes matching I<never> a bit
335easier to read.
336
337=item archname
338
339The same as C<< { $_->arch->archname } >> - the archetype name is commonly
340used to match items, so this shortcut is provided.
341
342=item resist_xxx
343
344Resistancy values such as C<resist_physical>, C<resist_magic>,
345C<resists_fire> etc. are directly available (but can also be accessed via
346array syntax, i.e. C<resists[ATNR_FIRE]>).
347
348=item body_xxx_info and body_xxx_used
349
350Every body location (e.g. C<body_neck_info>, C<body_arm_used> etc.) can
351be accessed via these functions (these are aliases to more cumbersome C<< {
352$_->slot_info (body_xxx) } >> and C<slot_used> method calls).
353
354Example: (e.g. on a door) match only players that have no arms.
355
356 match type=PLAYER and body_arm_info=0
357
281=item has(condition) 358=item has(condition)
282 359
283True iff the object has a matching inventory object. 360True iff the object has a matching inventory object.
284 361
285=item count(match) 362=item count(match)
286 363
287Number of matching objects - the context object for the C<match> is the 364Number of matching objects - the context object for the C<match> is the
288currently tested object - you can override this with an C<in object> for 365currently tested object - you can override this with an C<in object> for
289example. 366example.
290
291=item match(match)
292
293An independent match - semantics like C<count>, except it only matters
294whether the match finds any object (which is faster).
295 367
296=item dump() 368=item dump()
297 369
298Dumps the object to the server log when executed, and evaluates to true. 370Dumps the object to the server log when executed, and evaluates to true.
299 371
314 386
315 match = chain 387 match = chain
316 | chain 'of' root 388 | chain 'of' root
317 root = 'object' | 'self' | 'source' | 'originator' 389 root = 'object' | 'self' | 'source' | 'originator'
318 chain = condition 390 chain = condition
319 | chain also deep 'in' set 391 | chain also deep 'in' modifier
320 also = nothing | 'also' 392 also = nothing | 'also'
321 deep = nothing | 'deep' 393 deep = nothing | 'deep'
322 set = 'inv' | 'env' | 'arch' | 'map' 394 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
323 395
324 empty = 396 nothing =
325 397
326 # boolean matching condition 398 # boolean matching condition
327 399
328 condition = factor 400 condition = factor
329 | factor 'and'? condition 401 | factor 'and'? condition
330 | factor 'or' condition 402 | factor 'or' condition
331 403
332 factor = 'not' factor 404 factor = 'not' factor
333 | '(' condition ')' 405 | '(' match ')'
334 | expr 406 | expr
335 | expr operator constant 407 | expr operator constant
336 408
337 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 409 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
338 410
339 expr = flag 411 expr = flag
340 | sattr 412 | sattr
341 | aattr '[' <constant> ']' 413 | aattr '[' <constant> ']'
414 | 'stat.' statattr
342 | special 415 | special
343 | func '(' args ')' 416 | func '(' args ')'
344 | '{' perl code block '}' 417 | '{' perl code block '}'
345 418
346 func = <any function name> 419 func = <any function name>
347 sattr = <any scalar object attribute> 420 sattr = <any scalar object attribute>
348 aattr = <any array object attribute> 421 aattr = <any array object attribute>
349 flag = <any object flag> 422 flag = <any object flag>
423 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
350 special = <any ()-less "function"> 424 special = <any ()-less "function">
351 425
352 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 426 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
353 args = <depends on function> 427 args = <depends on function>
354 428
366 440
367use common::sense; 441use common::sense;
368 442
369use List::Util (); 443use List::Util ();
370 444
371# parser state
372# $_ # string to be parsed
373our $all; # find all, or just the first matching object
374
375{ 445{
376 package cf::match::exec; 446 package cf::match::exec;
377 447
378 use List::Util qw(first); 448 use List::Util qw(first);
379 449
383 453
384 sub ws { 454 sub ws {
385 /\G\s+/gc; 455 /\G\s+/gc;
386 } 456 }
387 457
458 sub condition ();
459 sub match ($$);
460
388 our %func = ( 461 our %func = (
389 has => sub { 462 has => sub {
390 'first { ' . &condition . ' } $_->inv' 463 'first { ' . condition . ' } $_->inv'
391 }, 464 },
392 count => sub { 465 count => sub {
393 local $all = 1;
394 '(scalar ' . &match ('$_') . ')' 466 '(scalar ' . (match 1, '$_') . ')'
395 },
396 match => sub {
397 local $all = 0;
398 '(scalar ' . &match ('$_') . ')'
399 }, 467 },
400 dump => sub { 468 dump => sub {
401 'do { 469 'do {
402 warn "cf::match::match dump:\n" 470 warn "cf::match::match dump:\n"
403 . "self: " . eval { $self->name } . "\n" 471 . "self: " . eval { $self->name } . "\n"
409 477
410 our %special = ( 478 our %special = (
411 any => sub { 479 any => sub {
412 1 480 1
413 }, 481 },
482 none => sub {
483 0
484 },
485 archname => sub {
486 '$_->arch->archname'
487 },
414 ); 488 );
489
490 # resist_xxx
491 for my $atnr (0 .. cf::NROFATTACKS - 1) {
492 $special{"resist_" . cf::attacktype_name ($atnr)} = sub { "\$_->resist ($atnr)" };
493 }
494
495 # body_xxx_info and _used
496 for my $slot (0 .. cf::NUM_BODY_LOCATIONS - 1) {
497 my $name = cf::object::slot_name $slot;
498
499 $special{"body_$name\_info"} = sub { "\$_->slot_info ($slot)" };
500 $special{"body_$name\_used"} = sub { "\$_->slot_used ($slot)" };
501 }
415 502
416 sub constant { 503 sub constant {
417 ws; 504 ws;
418 505
419 return $1 if /\G([\-\+0-9\.]+)/gc; 506 return $1 if /\G([\-\+0-9\.]+)/gc;
426 } 513 }
427 514
428 our $flag = $cf::REFLECT{object}{flags}; 515 our $flag = $cf::REFLECT{object}{flags};
429 our $sattr = $cf::REFLECT{object}{scalars}; 516 our $sattr = $cf::REFLECT{object}{scalars};
430 our $aattr = $cf::REFLECT{object}{arrays}; 517 our $aattr = $cf::REFLECT{object}{arrays};
518 our $lattr = $cf::REFLECT{living}{scalars};
431 519
432 sub expr { 520 sub expr {
433 # ws done by factor 521 # ws done by factor
434 my $res; 522 my $res;
435 523
438 526
439 my $expr = $1; 527 my $expr = $1;
440 528
441 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 529 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
442 530
531 } elsif (/\Gstats\.([A-Za-z0-9_]+)/gc) {
532
533 if (exists $lattr->{$1}) {
534 $res .= "\$_->stats->$1";
535 } elsif (exists $lattr->{"\u$1"}) {
536 $res .= "\$_->stats->\u$1";
537 } else {
538 die "living statistic name expected (str, pow, hp, sp...)\n";
539 }
540
443 } elsif (/\G([A-Za-z0-9_]+)/gc) { 541 } elsif (/\G([A-Za-z0-9_]+)/gc) {
444 542
445 if (my $func = $func{$1}) { 543 if (my $func = $func{$1}) {
446 /\G\s*\(/gc 544 /\G\s*\(/gc
447 or die "'(' expected after function name\n"; 545 or die "'(' expected after function name\n";
475 } else { 573 } else {
476 $res .= constant; 574 $res .= constant;
477 } 575 }
478 576
479 } else { 577 } else {
578 Carp::cluck;#d#
480 die "expr expected\n"; 579 die "expr expected\n";
481 } 580 }
482 581
483 $res 582 $res
484 } 583 }
501 $res .= "!"; 600 $res .= "!";
502 } 601 }
503 602
504 if (/\G\(/gc) { 603 if (/\G\(/gc) {
505 # () 604 # ()
506 $res .= &condition; 605
507 ws; 606 $res .= '(' . (match 0, '$_') . ')';
607
508 /\G\)/gc or die "')' expected\n"; 608 /\G\s*\)/gc or die "closing ')' expected\n";
509 609
510 } else { 610 } else {
511 my $expr = expr; 611 my $expr = expr;
512 612
513 $res .= $expr; 613 $res .= $expr;
524 } 624 }
525 625
526 "($res)" 626 "($res)"
527 } 627 }
528 628
529 sub condition { 629 sub condition () {
530 my $res = factor; 630 my $res = factor;
531 631
532 while () { 632 while () {
533 ws; 633 ws;
534 634
535 # first check some stop-symbols, so we don't have to backtrack 635 # first check some stop-symbols, so we don't have to backtrack
536 if (/\G(?=also\b|deep\b|in\b|of\b\)|\z)/gc) { 636 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
537 pos = pos; # argh. the misop hits again. again. again. again. you die. 637 pos = pos; # argh. the misop hits again. again. again. again. you die.
538 last; 638 last;
539 639
540 } elsif (/\Gor\b/gc) { 640 } elsif (/\Gor\b/gc) {
541 $res .= " || "; 641 $res .= " || ";
548 } 648 }
549 649
550 $res 650 $res
551 } 651 }
552 652
553 sub match { 653 sub match ($$) {
554 my $default = shift; 654 my ($wantarray, $defctx) = @_;
555 655
656 my $res = condition;
657
658 # if nothing follows, we have a simple condition, so
659 # optimise a comon case.
660 if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) {
661 return $wantarray
662 ? "$res ? \$_ : ()"
663 : $res;
664 }
665
556 my $res = ($all ? " grep { " : " first {") . condition . " }"; 666 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
557 667
558 while () { 668 while () {
559 ws; 669 ws;
560 670
561 my $also = /\Galso\s+/gc + 0; 671 my $also = /\Galso\s+/gc + 0;
562 my $deep = /\Gdeep\s+/gc + 0; 672 my $deep = /\Gdeep\s+/gc + 0;
563 673
564 if (/\Gin\s+/gc) { 674 if (/\Gin\s+/gc) {
565 my $expand; 675 my $expand;
566 676
567 if (/\G(inv|env|map|arch)\b/gc) { 677 if (/\G(inv|env|map|arch|head)\b/gc) {
568 if ($1 eq "inv") { 678 if ($1 eq "inv") {
569 $expand = "map \$_->inv,"; 679 $expand = "map \$_->inv,";
570 } elsif ($1 eq "env") { 680 } elsif ($1 eq "env") {
571 $expand = "map \$_->env // (),"; 681 $expand = "map \$_->env // (),";
682 } elsif ($1 eq "head") {
683 $expand = "map \$_->head,";
684 $deep = 0; # infinite loop otherwise
572 } elsif ($1 eq "arch") { 685 } elsif ($1 eq "arch") {
573 $expand = "map \$_->arch,"; 686 $expand = "map \$_->arch,";
687 $deep = 0; # infinite loop otherwise
574 } elsif ($1 eq "map") { 688 } elsif ($1 eq "map") {
575 $expand = "map \$_->map->at (\$_->x, \$_->y),"; 689 $expand = "map \$_->map->at (\$_->x, \$_->y),";
690 $deep = 0; # infinite loop otherwise
576 } 691 }
577 } else { 692 } else {
578 $expand = "map \$_->inv, grep { " . condition . " }"; 693 $expand = "map \$_->inv, grep { " . condition . " }";
579 } 694 }
580 695
588 $res .= " (\@res, \@_)\n" 703 $res .= " (\@res, \@_)\n"
589 . "}"; 704 . "}";
590 } else { 705 } else {
591 $res .= " $expand"; 706 $res .= " $expand";
592 } 707 }
708 } else {
709
593 } elsif (/\Gof\s+(self|object|source|originator)\b/gc) { 710 if (/\Gof\s+(self|object|source|originator)\b/gc) {
594 $also || $deep 711 $also || $deep
595 and die "neither 'also' nor 'deep' can be used with 'of'\n"; 712 and die "neither 'also' nor 'deep' can be used with 'of'\n";
596 713
597 if ($1 eq "self") { 714 if ($1 eq "self") {
598 return "$res \$self // ()"; 715 return "$res \$self // ()";
599 } elsif ($1 eq "object") { 716 } elsif ($1 eq "object") {
600 return "$res \$object"; 717 return "$res \$object";
601 } elsif ($1 eq "source") { 718 } elsif ($1 eq "source") {
602 return "$res \$source // ()"; 719 return "$res \$source // ()";
603 } elsif ($1 eq "originator") { 720 } elsif ($1 eq "originator") {
604 return "$res \$originator // \$source // ()"; 721 return "$res \$originator // \$source // ()";
722 }
723 } else {
724 return "$res $defctx";
605 } 725 }
606 } else {
607 return "$res $default";
608 } 726 }
609 } 727 }
610 } 728 }
611} 729}
612 730
613sub parse($;$) { 731sub parse($$) { # wantarray, matchexpr
614 local $_ = shift;
615 local $all = shift;
616
617 my $res; 732 my $res;
618 733
734 local $_ = $_[1];
735
619 eval { 736 eval {
620 $res = cf::match::parser::match "\$object"; 737 $res = cf::match::parser::match $_[0], "\$object";
621 738
622 /\G$/gc 739 /\G$/gc
623 or die "unexpected trailing characters after match\n"; 740 or die "unexpected trailing characters after match\n";
624 }; 741 };
625 742
634 751
635 $res 752 $res
636} 753}
637 754
638if (0) {#d# 755if (0) {#d#
639 die parse 'type=SPELL_EFFECT and match(name="bullet" in arch)', 1; 756 die parse 1, 'type=PLAYER and body_arm_info=0';
640 exit 0; 757 exit 0;
641} 758}
642 759
643=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
644
645Compiles (and caches) the C<$match> expression and matches it against
646the C<$object>. C<$self> should be the object initiating the match (or
647C<undef>), C<$source> should be the actor/source and C<$originator> the
648object that initiated the action (such as the player). C<$originator>
649defaults to C<$source> when not given.
650
651In list context it finds and returns all matching objects, in scalar
652context only a true or false value.
653
654=cut
655
656our %CACHE; 760our %CACHE;
657 761
658sub compile($$) { 762sub compile($$) {
659 my ($match, $all) = @_; 763 my ($wantarray, $match) = @_;
660 my $expr = parse $match, $all; 764 my $expr = parse $wantarray, $match;
661 warn "MATCH DEBUG $match,$all => $expr\n";#d# 765# warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
662 $expr = eval " 766 $expr = eval "
663 package cf::match::exec; 767 package cf::match::exec;
664 sub { 768 sub {
665 my (\$object, \$self, \$source, \$originator) = \@_; 769 my (\$object, \$self, \$source, \$originator) = \@_;
666 $expr 770 $expr
669 die if $@; 773 die if $@;
670 774
671 $expr 775 $expr
672} 776}
673 777
778=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
779
780Compiles (and caches) the C<$match> expression and matches it against
781the C<$object>. C<$self> should be the object initiating the match (or
782C<undef>), C<$source> should be the actor/source and C<$originator> the
783object that initiated the action (such as the player). C<$originator>
784defaults to C<$source> when not given.
785
786In list context it finds and returns all matching objects, in scalar
787context only a true or false value.
788
789=cut
790
674sub match($$;$$$) { 791sub match($$;$$$) {
675 my $match = shift; 792 my $match = shift;
676 my $all = wantarray+0; 793 my $wantarray = wantarray+0;
677 794
678 &{ 795 &{
679 $CACHE{"$all$match"} ||= compile $match, $all 796 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
680 } 797 }
681} 798}
799
800our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
801 %CACHE = ();
802};
682 803
683#d# $::schmorp=cf::player::find "schmorp"& 804#d# $::schmorp=cf::player::find "schmorp"&
684#d# cf::match::match '', $::schmorp->ob 805#d# cf::match::match '', $::schmorp->ob
685 806
686 807

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines