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.30 by root, Mon Oct 11 17:10:10 2010 UTC

1#
2# This file is part of Deliantra, the Roguelike Realtime MMORPG.
3#
4# Copyright (©) 2009,2010 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>
110 154
111=item in env 155=item in env
112 156
113Replaces all objects by their containing object, if they have one. 157Replaces all objects by their containing object, if they have one.
114 158
159=item in arch
160
161Replaces all objects by their archetypes.
162
115=item in map 163=item in map
116 164
117Replaces 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.
118 166
167=item in head
168
169Replaces all objects by their head objects.
170
119=item in <cond> 171=item in <condition>
120 172
121Finds all context objects matching the condition, and then puts their 173Finds all context objects matching the condition, and then puts their
122inventories into the context set. 174inventories into the context set.
123 175
124Note that C<in inv> is simply a special case of an C<< in <cond> >> that 176Note that C<in inv> is simply a special case of an C<< in <condition> >> that
125matches any object. 177matches any object.
126 178
127Example: find all spells inside potions inside the inventory of the context 179Example: find all spells inside potions inside the inventory of the context
128object(s). 180object(s).
129 181
136 188
137Example: 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.
138 190
139 type=SPELL also in inv 191 type=SPELL also in inv
140 192
141=item deep in ... 193=item also deep in ...
142 194
143Repeats 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
144recursively look into objects. 196recursively look into objects.
145 197
146=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.
147 201
148C<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.
149 204
150Example: check if there are any unpaid items in an inventory, 205Example: check if there are any unpaid items in an inventory,
151or in the inventories of the inventory objects, and so on. 206or in the inventories of the inventory objects, and so on.
152 207
153 unpaid also deep in inv 208 unpaid also deep in inv
174Starts 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
175match against by default. Matches have an explicit C<of object> appended, 230match against by default. Matches have an explicit C<of object> appended,
176but 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>
177can be used to start at the original object once more. 232can be used to start at the original object once more.
178 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
179=item of source 239=item of source
180 240
181Starts with the I<source> object - this object is sometimes passed to 241Starts with the I<source> object - this object is sometimes passed to
182matches 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
183as 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
190the original initiator of an action, most commonly a player or monster. 250the original initiator of an action, most commonly a player or monster.
191 251
192This 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
193a spell, the player is both source and originator). 253a spell, the player is both source and originator).
194 254
195=item of self
196
197Starts with the object initiating/asking for the match - this is basically
198always the object that the match expression is attached to.
199
200=back 255=back
201 256
202=head2 EXPRESSIONS 257=head2 EXPRESSIONS
203 258
204Expressions used in conditions usually consist of simple boolean checks 259Expressions used in conditions usually consist of simple boolean checks
213 268
214=item scalar object attributes 269=item scalar object attributes
215 270
216Object attributes that consist of a single value (C<name>, C<title>, 271Object attributes that consist of a single value (C<name>, C<title>,
217C<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
218acse their corresponding value is used. 273case their corresponding value is used.
219 274
220=item array objects attributes 275=item array objects attributes
221 276
222The 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 ]
223>>. 278>>.
227 resist[ATNR_ACID] > 30 282 resist[ATNR_ACID] > 30
228 283
229=item functions 284=item functions
230 285
231Some additional functions with or without arguments in parentheses are 286Some additional functions with or without arguments in parentheses are
232available. 287available. They are documented in their own section, below.
233 288
234=item { BLOCK } 289=item { BLOCK }
235 290
236You can specify perl code to execute by putting it inside curly 291You can specify perl code to execute by putting it inside curly
237braces. The last expression evaluated inside will become the result. 292braces. The last expression evaluated inside will become the result.
272=item any 327=item any
273 328
274This 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
275bit easier to read. 330bit easier to read.
276 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
277=item has(condition) 342=item has(condition)
278 343
279True iff the object has a matching inventory object. 344True iff the object has a matching inventory object.
280 345
281=item count(match) 346=item count(match)
282 347
283Number of matching objects - the context object for the C<match> is the 348Number 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 349currently tested object - you can override this with an C<in object> for
285example. 350example.
286 351
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 352=item dump()
293 353
294Dumps the object to the server log when executed, and evaluates to true. 354Dumps the object to the server log when executed, and evaluates to true.
295 355
296Note that logical operations are short-circuiting, so this only dumps 356Note that logical operations are short-circuiting, so this only dumps
297potions: 357potions:
298 358
299 type=POTION and dump 359 type=POTION and dump()
300 360
301=back 361=back
302 362
303=head2 GRAMMAR 363=head2 GRAMMAR
304 364
310 370
311 match = chain 371 match = chain
312 | chain 'of' root 372 | chain 'of' root
313 root = 'object' | 'self' | 'source' | 'originator' 373 root = 'object' | 'self' | 'source' | 'originator'
314 chain = condition 374 chain = condition
315 | chain also deep 'in' set 375 | chain also deep 'in' modifier
316 also = nothing | 'also' 376 also = nothing | 'also'
317 deep = nothing | 'deep' 377 deep = nothing | 'deep'
318 set = 'inv' | 'env' | 'map' 378 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
319 379
320 empty = 380 nothing =
321 381
322 # boolean matching condition 382 # boolean matching condition
323 383
324 condition = factor 384 condition = factor
325 | factor 'and'? cond 385 | factor 'and'? condition
326 | factor 'or' cond 386 | factor 'or' condition
327 387
328 factor = 'not' factor 388 factor = 'not' factor
329 | '(' cond ')' 389 | '(' match ')'
330 | expr 390 | expr
331 | expr operator constant 391 | expr operator constant
332 392
333 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 393 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
334 394
335 expr = flag 395 expr = flag
336 | sattr 396 | sattr
337 | aattr '[' <constant> ']' 397 | aattr '[' <constant> ']'
398 | 'stat.' statattr
338 | special 399 | special
339 | func '(' args ')' 400 | func '(' args ')'
340 | '{' perl code block '}' 401 | '{' perl code block '}'
341 402
342 func = <any function name> 403 func = <any function name>
343 sattr = <any scalar object attribute> 404 sattr = <any scalar object attribute>
344 aattr = <any array object attribute> 405 aattr = <any array object attribute>
345 flag = <any object flag> 406 flag = <any object flag>
407 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
346 special = <any ()-less "function"> 408 special = <any ()-less "function">
347 409
348 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 410 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
349 args = <depends on function> 411 args = <depends on function>
350 412
361package cf::match; 423package cf::match;
362 424
363use common::sense; 425use common::sense;
364 426
365use List::Util (); 427use List::Util ();
366
367# parser state
368# $_ # string to be parsed
369our $all; # find all, or just the first matching object
370 428
371{ 429{
372 package cf::match::exec; 430 package cf::match::exec;
373 431
374 use List::Util qw(first); 432 use List::Util qw(first);
379 437
380 sub ws { 438 sub ws {
381 /\G\s+/gc; 439 /\G\s+/gc;
382 } 440 }
383 441
442 sub condition ();
443 sub match ($$);
444
384 our %func = ( 445 our %func = (
385 has => sub { 446 has => sub {
386 'first { ' . &condition . ' } $_->inv' 447 'first { ' . condition . ' } $_->inv'
387 }, 448 },
388 count => sub { 449 count => sub {
389 local $all = 1;
390 '(scalar ' . &match ('$_') . ')' 450 '(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 }, 451 },
402 dump => sub { 452 dump => sub {
403 'do { 453 'do {
404 warn "cf::match::match dump:\n" 454 warn "cf::match::match dump:\n"
405 . "self: " . eval { $self->name } . "\n" 455 . "self: " . eval { $self->name } . "\n"
407 1 457 1
408 }'; 458 }';
409 }, 459 },
410 ); 460 );
411 461
462 our %special = (
463 any => sub {
464 1
465 },
466 none => sub {
467 0
468 },
469 archname => sub {
470 '$_->arch->archname'
471 },
472 );
473
412 sub constant { 474 sub constant {
413 ws; 475 ws;
414 476
415 return $1 if /\G([\-\+0-9\.]+)/gc; 477 return $1 if /\G([\-\+0-9\.]+)/gc;
416 return "cf::$1" if /\G([A-Z0-9_]+)/gc; 478 return "cf::$1" if /\G([A-Z0-9_]+)/gc;
422 } 484 }
423 485
424 our $flag = $cf::REFLECT{object}{flags}; 486 our $flag = $cf::REFLECT{object}{flags};
425 our $sattr = $cf::REFLECT{object}{scalars}; 487 our $sattr = $cf::REFLECT{object}{scalars};
426 our $aattr = $cf::REFLECT{object}{arrays}; 488 our $aattr = $cf::REFLECT{object}{arrays};
489 our $lattr = $cf::REFLECT{living}{scalars};
427 490
428 sub expr { 491 sub expr {
429 # ws done by factor 492 # ws done by factor
430 my $res; 493 my $res;
431 494
434 497
435 my $expr = $1; 498 my $expr = $1;
436 499
437 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 500 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
438 501
502 } elsif (/\Gstats\.([A-Za-z0-9_]+)/gc) {
503
504 if (exists $lattr->{$1}) {
505 $res .= "\$_->stats->$1";
506 } elsif (exists $lattr->{"\u$1"}) {
507 $res .= "\$_->stats->\u$1";
508 } else {
509 die "living statistic name expected (str, pow, hp, sp...)\n";
510 }
511
439 } elsif (/\G([A-Za-z0-9_]+)/gc) { 512 } elsif (/\G([A-Za-z0-9_]+)/gc) {
440 513
441 if (my $func = $func{$1}) { 514 if (my $func = $func{$1}) {
442 /\G\s*\(/gc 515 /\G\s*\(/gc
443 or die "'(' expected after function name\n"; 516 or die "'(' expected after function name\n";
471 } else { 544 } else {
472 $res .= constant; 545 $res .= constant;
473 } 546 }
474 547
475 } else { 548 } else {
549 Carp::cluck;#d#
476 die "expr expected\n"; 550 die "expr expected\n";
477 } 551 }
478 552
479 $res 553 $res
480 } 554 }
497 $res .= "!"; 571 $res .= "!";
498 } 572 }
499 573
500 if (/\G\(/gc) { 574 if (/\G\(/gc) {
501 # () 575 # ()
502 $res .= &condition; 576
503 ws; 577 $res .= '(' . (match 0, '$_') . ')';
578
504 /\G\)/gc or die "')' expected\n"; 579 /\G\s*\)/gc or die "closing ')' expected\n";
505 580
506 } else { 581 } else {
507 my $expr = expr; 582 my $expr = expr;
508 583
509 $res .= $expr; 584 $res .= $expr;
520 } 595 }
521 596
522 "($res)" 597 "($res)"
523 } 598 }
524 599
525 sub condition { 600 sub condition () {
526 my $res = factor; 601 my $res = factor;
527 602
528 while () { 603 while () {
529 ws; 604 ws;
530 605
531 # first check some stop-symbols, so we don't have to backtrack 606 # first check some stop-symbols, so we don't have to backtrack
532 if (/\G(?=also\b|deep\b|in\b|of\b\)|$)/gc) { 607 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
608 pos = pos; # argh. the misop hits again. again. again. again. you die.
533 last; 609 last;
534 610
535 } elsif (/\Gor\b/gc) { 611 } elsif (/\Gor\b/gc) {
536 $res .= " || "; 612 $res .= " || ";
537 613
543 } 619 }
544 620
545 $res 621 $res
546 } 622 }
547 623
548 sub match { 624 sub match ($$) {
549 my $default = shift; 625 my ($wantarray, $defctx) = @_;
550 626
627 my $res = condition;
628
629 # if nothing follows, we have a simple condition, so
630 # optimise a comon case.
631 if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) {
632 return $wantarray
633 ? "$res ? \$_ : ()"
634 : $res;
635 }
636
551 my $res = ($all ? " grep { " : " first {") . condition . " }"; 637 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
552 638
553 while () { 639 while () {
554 ws; 640 ws;
555 641
556 my $also = /\Galso\s+/gc + 0; 642 my $also = /\Galso\s+/gc + 0;
557 my $deep = /\Gdeep\s+/gc + 0; 643 my $deep = /\Gdeep\s+/gc + 0;
558 644
559 if (/\Gin\s+/gc) { 645 if (/\Gin\s+/gc) {
560 my $expand; 646 my $expand;
561 647
562 if (/\G(inv|env|map)\b/gc) { 648 if (/\G(inv|env|map|arch|head)\b/gc) {
563 if ($1 eq "inv") { 649 if ($1 eq "inv") {
564 $expand = "map \$_->inv,"; 650 $expand = "map \$_->inv,";
565 } elsif ($1 eq "env") { 651 } elsif ($1 eq "env") {
566 $expand = "map \$_->env // (),"; 652 $expand = "map \$_->env // (),";
653 } elsif ($1 eq "head") {
654 $expand = "map \$_->head,";
655 $deep = 0; # infinite loop otherwise
656 } elsif ($1 eq "arch") {
657 $expand = "map \$_->arch,";
658 $deep = 0; # infinite loop otherwise
567 } elsif ($1 eq "map") { 659 } elsif ($1 eq "map") {
568 $expand = "map \$_->map->at (\$_->x, \$_->y),"; 660 $expand = "map \$_->map->at (\$_->x, \$_->y),";
661 $deep = 0; # infinite loop otherwise
569 } 662 }
570 } else { 663 } else {
571 $expand = "map \$_->inv, grep { " . condition . " }"; 664 $expand = "map \$_->inv, grep { " . condition . " }";
572 } 665 }
573 666
581 $res .= " (\@res, \@_)\n" 674 $res .= " (\@res, \@_)\n"
582 . "}"; 675 . "}";
583 } else { 676 } else {
584 $res .= " $expand"; 677 $res .= " $expand";
585 } 678 }
679 } else {
680
586 } elsif (/\Gof\s+(self|object|source|originator)\b/gc) { 681 if (/\Gof\s+(self|object|source|originator)\b/gc) {
587 $also || $deep 682 $also || $deep
588 and die "neither 'also' nor 'deep' can be used with 'of'\n"; 683 and die "neither 'also' nor 'deep' can be used with 'of'\n";
589 684
590 if ($1 eq "self") { 685 if ($1 eq "self") {
591 return "$res \$self // ()"; 686 return "$res \$self // ()";
592 } elsif ($1 eq "object") { 687 } elsif ($1 eq "object") {
593 return "$res \$object"; 688 return "$res \$object";
594 } elsif ($1 eq "source") { 689 } elsif ($1 eq "source") {
595 return "$res \$source // ()"; 690 return "$res \$source // ()";
596 } elsif ($1 eq "originator") { 691 } elsif ($1 eq "originator") {
597 return "$res \$originator // \$source // ()"; 692 return "$res \$originator // \$source // ()";
693 }
694 } else {
695 return "$res $defctx";
598 } 696 }
599 } else {
600 return "$res $default";
601 } 697 }
602 } 698 }
603 } 699 }
604} 700}
605 701
606sub parse($;$) { 702sub parse($$) { # wantarray, matchexpr
607 local $_ = shift;
608 local $all = shift;
609
610 my $res; 703 my $res;
611 704
705 local $_ = $_[1];
706
612 eval { 707 eval {
613 $res = cf::match::parser::match "\$object"; 708 $res = cf::match::parser::match $_[0], "\$object";
614 709
615 /\G\s*$/gc 710 /\G$/gc
616 or die "unexpected trailing characters after match\n"; 711 or die "unexpected trailing characters after match\n";
617 }; 712 };
618 713
619 if ($@) { 714 if ($@) {
620 my $ctx = 20; 715 my $ctx = 20;
627 722
628 $res 723 $res
629} 724}
630 725
631if (0) {#d# 726if (0) {#d#
632 die parse 'applied', 1; 727 die parse 1, 'stats.pow';
633 exit 0; 728 exit 0;
634} 729}
635 730
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; 731our %CACHE;
650 732
651sub compile($$) { 733sub compile($$) {
652 my ($match, $all) = @_; 734 my ($wantarray, $match) = @_;
653 my $expr = parse $match, $all; 735 my $expr = parse $wantarray, $match;
654 warn "MATCH DEBUG $match,$all => $expr\n";#d# 736# warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
655 $expr = eval " 737 $expr = eval "
656 package cf::match::exec; 738 package cf::match::exec;
657 sub { 739 sub {
658 my (\$object, \$self, \$source, \$originator) = \@_; 740 my (\$object, \$self, \$source, \$originator) = \@_;
659 $expr 741 $expr
662 die if $@; 744 die if $@;
663 745
664 $expr 746 $expr
665} 747}
666 748
749=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
750
751Compiles (and caches) the C<$match> expression and matches it against
752the C<$object>. C<$self> should be the object initiating the match (or
753C<undef>), C<$source> should be the actor/source and C<$originator> the
754object that initiated the action (such as the player). C<$originator>
755defaults to C<$source> when not given.
756
757In list context it finds and returns all matching objects, in scalar
758context only a true or false value.
759
760=cut
761
667sub match($$;$$$) { 762sub match($$;$$$) {
668 my $match = shift; 763 my $match = shift;
669 my $all = wantarray+0; 764 my $wantarray = wantarray+0;
670 765
671 &{ 766 &{
672 $CACHE{"$all$match"} ||= compile $match, $all 767 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
673 } 768 }
674} 769}
675 770
771our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
772 %CACHE = ();
773};
774
676#d# $::schmorp=cf::player::find "schmorp"& 775#d# $::schmorp=cf::player::find "schmorp"&
677#d# cf::match::match '', $::schmorp->ob 776#d# cf::match::match '', $::schmorp->ob
678 777
679 778
680=back 779=back

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines