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.6 by root, Sat Oct 10 05:33:08 2009 UTC vs.
Revision 1.24 by root, Tue Nov 3 23:44:21 2009 UTC

1#
2# This file is part of Deliantra, the Roguelike Realtime MMORPG.
3#
4# Copyright (©) 2009 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
6 28
7This module implements a simple object matching language. It can be asked 29This module implements a simple object matching language. It can be asked
8to find any (boolean context), or all (list context), matching objects. 30to find any ("check for a match"), or all ("find all objects") matching
31objects.
9 32
10=head1 MATCH EXAMPLES 33=head1 MATCH EXAMPLES
11 34
12Match the object if it has a slaying field of C<key1>: 35Match the object if it has a slaying field of C<key1>:
13 36
24 47
25Find all potions with spell objects inside them in someones inventory: 48Find all potions with spell objects inside them in someones inventory:
26 49
27 type=SPELL in type=POTION in inv 50 type=SPELL in type=POTION in inv
28 51
29Find all potions inside someones inventory, or inside applied containers: 52Find all scrolls inside someones inventory, or inside applied scroll
53containers:
30 54
31 type=POTION also in type=CONTAINER and applied in inv 55 type=SCROLL also in applied type=CONTAINER race="scroll" in inv
32 56
33=head1 LANGUAGE 57Find all unpaid items, anywhere, even deeply nested inside other items, in
58the originator:
34 59
35 # object selection 60 unpaid also deep in inv of originator
36 61
37 select = set 62=head1 MATCH EXPRESSIONS
38 | select also rep 'in' set
39 also = nothing | 'also'
40 rep = nothing | 'rep' | 'repeatedly'
41
42 set = 'inv' | 'env' | 'map'
43
44 empty =
45
46 # object matching
47
48 match = factor
49 | factor 'and'? match
50 | factor 'or' match
51
52 factor = 'not' factor
53 | '(' match ')'
54 | expr
55 | expr operator constant
56
57 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
58
59 expr = flag
60 | sattr
61 | aattr '[' <constant> ']'
62 | special
63 | func '(' args ')'
64 | '{' perl code block '}'
65
66 func = <any function name>
67 sattr = <any scalar object attribute>
68 aattr = <any array object attribute>
69 flag = <any object flag>
70 special = <any ()-less "function">
71
72 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
73 args = <depends on function>
74
75 TODO: repeatedly, env, contains, possbly matches
76 63
77=head2 STRUCTURE 64=head2 STRUCTURE
78 65
79The two main structures are the C<select>, which selects objects matching 66The two main structures are the C<match>, which selects objects matching
80various criteria, and the C<match>, which determines if an object matches 67various criteria, and the C<condition, which determines if an object
81some desired properties. 68matches some desired properties:
82 69
70 condition
71 condition in set-modifier
72 condition of root-object
73
83A C<select> is passed a set of "context objects" that it is applied 74A C<condition> receives a set of "context objects" that it is applied
84to. This is initially just one object - for altars, it is the object 75to. This is initially just one object - by default, for altars, it is the
85dropped on it, for pedestals, the object on top of it and so on. 76object dropped on it, for pedestals, the object on top of it and so on.
86 77
87This set of context objects can be modified in various ways, for example 78This set of context objects can be modified in various ways, for example
88by replacing it with the inventories of all objects, or all objects on the 79by replacing it with the inventories of all objects, or all items on the
89same mapspace, and so on, by using the C<in> operator. 80same mapspace, and so on, by using the C<in> operator:
90 81
82 condition in inv
83 condition in map
84
85Also, besides the default root object where all this begins, you can start
86elsewhere, for example in the I<originator> (usually the player):
87
88 condition in inv of originator
89
91Once the set of context objects has been established, each object is 90Once the final set of context objects has been established, each object
92matched against the C<match> expression. Sometimes the server is only 91is matched against the C<condition>.
93interested in knowing whether I<anything> matches, and sometimes the 92
94server is interested in I<all> objects that match. 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
100Sometimes the server is only interested in knowing whether I<anything>
101matches, and sometimes the server is interested in I<all> objects that
102match.
95 103
96=head2 OPERATORS 104=head2 OPERATORS
97 105
98=over 4 106=over 4
99 107
100=item and, or, not, () 108=item and, or, not, ()
101 109
102Match expressions 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
103expressions. C<not> negates the expression, and parentheses can be used to 111expressions. C<not> negates the condition, and parentheses can be used to
104group match expressions. 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)
105 124
106Example: match applied weapons. 125Example: match applied weapons.
107 126
108 type=WEAPON and applied 127 applied type=WEAPON
109 128
110Example: match horns or rods. 129Example: match horns or rods.
111 130
112 type=HORN or type=ROD 131 type=HORN or type=ROD
113 132
133Example: see if the originator is a player.
134
135 type=PLAYER of originator
136
114=item in ... 137=item in ...
115 138
116The in operator takes the context set and modifies it in various ways. 139The in operator takes the context set and modifies it in various ways. As
140a less technical description, think of the C<in> as being a I<look into>
141or I<look at> operator - instead of looking at whatever was provided to
142the match, the C<in> operator lets you look at other sets of objects, most
143often the inventory.
117 144
118=over 4 145=over 4
119 146
120=item in inv 147=item in inv
121 148
127 154
128=item in env 155=item in env
129 156
130Replaces all objects by their containing object, if they have one. 157Replaces all objects by their containing object, if they have one.
131 158
159=item in arch
160
161Replaces all objects by their archetypes.
162
132=item in map 163=item in map
133 164
134Replaces 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.
135 166
136=item in <match> 167=item in head
137 168
169Replaces all objects by their head objects.
170
171=item in <condition>
172
138Finds all context objects matching the match expression, and then puts 173Finds all context objects matching the condition, and then puts their
139their inventories into the context set. 174inventories into the context set.
140 175
141Note that C<in inv> is simply a special case of an C<< in <match> >> that 176Note that C<in inv> is simply a special case of an C<< in <condition> >> that
142matches any object. 177matches any object.
143 178
144Example: find all spells inside potions inside the inventory of the context 179Example: find all spells inside potions inside the inventory of the context
145object(s). 180object(s).
146 181
153 188
154Example: 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.
155 190
156 type=SPELL also in inv 191 type=SPELL also in inv
157 192
158=item repeatedly in ... 193=item also deep in ...
159 194
160Repeats 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
161recursively look into objects. 196recursively look into objects.
162 197
163=item also repeatedly 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.
164 201
165C<also> and C<repeatedly> can be combined. 202Similarly, C<also deep in env> means to take the environment object, their
203environemnt object and so on.
166 204
167Example: check if there are any unpaid items in an inventory, 205Example: check if there are any unpaid items in an inventory,
168or in the inventories of the inventory objects, and so on. 206or in the inventories of the inventory objects, and so on.
169 207
170 unpaid also repeatedly in inv 208 unpaid also deep in inv
171 209
172Example: check if a object is inside a player. 210Example: check if a object is inside a player.
173 211
174 type=PLAYER also repeatedly in env 212 type=PLAYER also deep in env
175 213
176=back 214=back
177 215
216=item of ...
217
218By default, all matches are applied to the "obviously appropriate" object,
219such as the item dropped on a button or moving over a detector. This can
220be changed to a number of other objects - not all of them are available
221for each match (when not available, the match will simply fail).
222
223An C<of> term ends a match, nothing is allowed to follow.
224
225=over 4
226
227=item of object
228
229Starts with the default object - this is the object passed to the match to
230match against by default. Matches have an explicit C<of object> appended,
231but submatches start at the current object, and in this case C<of object>
232can be used to start at the original object once more.
233
234=item of source
235
236Starts with the I<source> object - this object is sometimes passed to
237matches and represents the object that is the source of the action, such
238as a rod or a potion when it is applied. Often, the I<source> is the same
239as the I<originator>.
240
241=item of originator
242
243Starts with the I<originator> - one step farther removed than the
244I<source>, the I<originator> is sometimes passed to matches and represents
245the original initiator of an action, most commonly a player or monster.
246
247This object is often identical to the I<source> (e.g. when a player casts
248a spell, the player is both source and originator).
249
250=item of self
251
252Starts with the object initiating/asking for the match - this is basically
253always the object that the match expression is attached to.
254
178=back 255=back
179 256
180=head2 EXPRESSIONS 257=head2 EXPRESSIONS
181 258
182Match expressions usually consist of simple boolean checks (flag XYZ is 259Expressions used in conditions usually consist of simple boolean checks
183set) or simple comparisons. 260(flag XYZ is set) or simple comparisons.
184 261
185=over 4 262=over 4
186 263
187=item flags 264=item flags
188 265
212=item { BLOCK } 289=item { BLOCK }
213 290
214You can specify perl code to execute by putting it inside curly 291You can specify perl code to execute by putting it inside curly
215braces. The last expression evaluated inside will become the result. 292braces. The last expression evaluated inside will become the result.
216 293
294The perlcode can access C<$_>, which rferes to the object currently being
295matches, and the C<$object>, C<$self>, C<$source> and C<$originator>.
296
297Example: check whether the slaying field consists of digits only.
298
299 { $_->slaying =~ /^\d+$/ }
300
217=item comparisons, <, <=, ==, =, !=, =>, > 301=item comparisons, <, <=, ==, =, !=, =>, >
218 302
219You can compare expressions against constants via any of these 303You can compare expressions against constants via any of these
220operators. If the constant is a string, then a string compare will be 304operators. If the constant is a string, then a string compare will be
221done, otherwise a numerical comparison is used. 305done, otherwise a numerical comparison is used.
243=item any 327=item any
244 328
245This 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
246bit easier to read. 330bit easier to read.
247 331
248=item has(match) 332=item none
333
334This simply evaluates to false, and simply makes matching I<never> a bit
335easier to read.
336
337=item has(condition)
249 338
250True iff the object has a matching inventory object. 339True iff the object has a matching inventory object.
251 340
252=item count(select) 341=item count(match)
253 342
254Number of matching objects - the context object for the C<select> are the 343Number of matching objects - the context object for the C<match> is the
255original context objects for the overall C<select>. # TODO bullshit 344currently tested object - you can override this with an C<in object> for
345example.
346
347=item dump()
348
349Dumps the object to the server log when executed, and evaluates to true.
350
351Note that logical operations are short-circuiting, so this only dumps
352potions:
353
354 type=POTION and dump()
256 355
257=back 356=back
258 357
358=head2 GRAMMAR
359
360This is the grammar that was used to implement the matching language
361module. It is meant to be easily readable by humans, not to implement it
362exactly as-is.
363
364 # object matching and selecting
365
366 match = chain
367 | chain 'of' root
368 root = 'object' | 'self' | 'source' | 'originator'
369 chain = condition
370 | chain also deep 'in' modifier
371 also = nothing | 'also'
372 deep = nothing | 'deep'
373 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
374
375 nothing =
376
377 # boolean matching condition
378
379 condition = factor
380 | factor 'and'? condition
381 | factor 'or' condition
382
383 factor = 'not' factor
384 | '(' match ')'
385 | expr
386 | expr operator constant
387
388 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
389
390 expr = flag
391 | sattr
392 | aattr '[' <constant> ']'
393 | 'stat.' statattr
394 | special
395 | func '(' args ')'
396 | '{' perl code block '}'
397
398 func = <any function name>
399 sattr = <any scalar object attribute>
400 aattr = <any array object attribute>
401 flag = <any object flag>
402 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
403 special = <any ()-less "function">
404
405 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
406 args = <depends on function>
407
408 TODO: contains, matches, query_name, selling_price, buying_price?
409
259=cut 410=cut
260 411
412=head2 PERL FUNCTIONS
413
414=over 4
415
416=cut
261 417
262package cf::match; 418package cf::match;
263 419
264use common::sense; 420use common::sense;
265 421
266use List::Util (); 422use List::Util ();
267
268# parser state
269# $_ # string to be parsed
270our $all; # find all, or just the first matching object
271 423
272{ 424{
273 package cf::match::exec; 425 package cf::match::exec;
274 426
275 our @ctx; # root object(s)
276
277 use List::Util qw(first); 427 use List::Util qw(first);
278
279 sub env_chain {
280 my @res;
281 push @res, $_
282 while $_ = $_->env;
283 @res
284 }
285 428
286 package cf::match::parser; 429 package cf::match::parser;
287 430
288 use common::sense; 431 use common::sense;
289 432
290 sub ws { 433 sub ws {
291 /\G\s+/gc; 434 /\G\s+/gc;
292 } 435 }
293 436
437 sub condition ();
438 sub match ($$);
439
294 our %func = ( 440 our %func = (
295 has => sub { 441 has => sub {
296 'first { ' . &match . ' } $_->inv' 442 'first { ' . condition . ' } $_->inv'
297 }, 443 },
298 count => sub { 444 count => sub {
299 local $all = 1; 445 '(scalar ' . (match 1, '$_') . ')'
300 '(scalar ' . &select . ')' 446 },
447 dump => sub {
448 'do {
449 warn "cf::match::match dump:\n"
450 . "self: " . eval { $self->name } . "\n"
451 . $_->as_string;
452 1
453 }';
301 }, 454 },
302 ); 455 );
303 456
304 our %special = ( 457 our %special = (
305 any => sub { 458 any => sub {
306 1 459 1
307 }, 460 },
461 none => sub {
462 0
463 },
308 ); 464 );
309 465
310 sub constant { 466 sub constant {
311 ws; 467 ws;
312 468
320 } 476 }
321 477
322 our $flag = $cf::REFLECT{object}{flags}; 478 our $flag = $cf::REFLECT{object}{flags};
323 our $sattr = $cf::REFLECT{object}{scalars}; 479 our $sattr = $cf::REFLECT{object}{scalars};
324 our $aattr = $cf::REFLECT{object}{arrays}; 480 our $aattr = $cf::REFLECT{object}{arrays};
481 our $lattr = $cf::REFLECT{living}{scalars};
325 482
326 sub expr { 483 sub expr {
327 # ws done by factor 484 # ws done by factor
328 my $res; 485 my $res;
329 486
332 489
333 my $expr = $1; 490 my $expr = $1;
334 491
335 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 492 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
336 493
494 } elsif (/\Gstats\.([A-Za-z0-9_]+)/gc) {
495
496 if (exists $lattr->{$1}) {
497 $res .= "\$_->stats->$1";
498 } elsif (exists $lattr->{"\u$1"}) {
499 $res .= "\$_->stats->\u$1";
500 } else {
501 die "living statistic name expected (str, pow, hp, sp...)\n";
502 }
503
337 } elsif (/\G([A-Za-z0-9_]+)/gc) { 504 } elsif (/\G([A-Za-z0-9_]+)/gc) {
338 505
339 if (my $func = $func{$1}) { 506 if (my $func = $func{$1}) {
340 /\G\s*\(/gc 507 /\G\s*\(/gc
341 or die "'(' expected after function name\n"; 508 or die "'(' expected after function name\n";
369 } else { 536 } else {
370 $res .= constant; 537 $res .= constant;
371 } 538 }
372 539
373 } else { 540 } else {
541 Carp::cluck;#d#
374 die "expr expected\n"; 542 die "expr expected\n";
375 } 543 }
376 544
377 $res 545 $res
378 } 546 }
395 $res .= "!"; 563 $res .= "!";
396 } 564 }
397 565
398 if (/\G\(/gc) { 566 if (/\G\(/gc) {
399 # () 567 # ()
400 $res .= &match; 568
401 ws; 569 $res .= '(' . (match 0, '$_') . ')';
570
402 /\G\)/gc or die "')' expected\n"; 571 /\G\s*\)/gc or die "closing ')' expected\n";
403 572
404 } else { 573 } else {
405 my $expr = expr; 574 my $expr = expr;
406 575
407 $res .= $expr; 576 $res .= $expr;
418 } 587 }
419 588
420 "($res)" 589 "($res)"
421 } 590 }
422 591
423 sub match { 592 sub condition () {
424 my $res = factor; 593 my $res = factor;
425 594
426 while () { 595 while () {
427 ws; 596 ws;
597
598 # first check some stop-symbols, so we don't have to backtrack
428 if (/\G(?=also\b|in\b|\)|$)/gc) { 599 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
429 # early stop => faster and requires no backtracking 600 pos = pos; # argh. the misop hits again. again. again. again. you die.
430 last; 601 last;
602
431 } elsif (/\Gor\b/gc) { 603 } elsif (/\Gor\b/gc) {
432 $res .= " || "; 604 $res .= " || ";
605
433 } else { 606 } else {
434 /\Gand\b/gc; 607 /\Gand\b/gc;
435 $res .= " && "; 608 $res .= " && ";
436 } 609 }
437 $res .= factor; 610 $res .= factor;
438 } 611 }
439 612
440 $res 613 $res
441 } 614 }
442 615
443 sub select { 616 sub match ($$) {
444 my $res; 617 my ($wantarray, $defctx) = @_;
445 618
446 my $also; # undef means first iteration 619 my $res = condition;
620
621 # if nothing follows, we have a simple condition, so
622 # optimise a comon case.
623 if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) {
624 return $wantarray
625 ? "$res ? \$_ : ()"
626 : $res;
627 }
628
629 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
630
447 while () { 631 while () {
632 ws;
633
634 my $also = /\Galso\s+/gc + 0;
635 my $deep = /\Gdeep\s+/gc + 0;
636
637 if (/\Gin\s+/gc) {
638 my $expand;
639
448 if (/\G\s*(inv|env|map)\b/gc) { 640 if (/\G(inv|env|map|arch|head)\b/gc) {
449 if ($1 eq "inv") { 641 if ($1 eq "inv") {
450 $res .= " map+(${also}\$_->inv),"; 642 $expand = "map \$_->inv,";
451 } elsif ($1 eq "env") { 643 } elsif ($1 eq "env") {
452 $res .= " map+(${also}env_chain), "; # TODO 644 $expand = "map \$_->env // (),";
645 } elsif ($1 eq "head") {
646 $expand = "map \$_->head,";
647 $deep = 0; # infinite loop otherwise
648 } elsif ($1 eq "arch") {
649 $expand = "map \$_->arch,";
650 $deep = 0; # infinite loop otherwise
453 } elsif ($1 eq "map") { 651 } elsif ($1 eq "map") {
454 $res .= " map+(${also}\$_->map->at (\$_->x, \$_->y)),"; 652 $expand = "map \$_->map->at (\$_->x, \$_->y),";
653 $deep = 0; # infinite loop otherwise
654 }
655 } else {
656 $expand = "map \$_->inv, grep { " . condition . " }";
455 } 657 }
456 last unless /\G\s*in\b/gc; 658
659 if ($also || $deep) {
660 $res .= " do {\n"
661 . " my \@res;\n";
662 $res .= " while (\@_) {\n" if $deep;
663 $res .= " push \@res, \@_;\n" if $also;
664 $res .= " \@_ = $expand \@_;\n";
665 $res .= " }\n" if $deep;
666 $res .= " (\@res, \@_)\n"
667 . "}";
668 } else {
669 $res .= " $expand";
670 }
457 } else { 671 } else {
458 $res .= " map+($also\$_->inv)," if defined $also;
459 $res .= $all ? " grep { " : " first {";
460 $res .= match;
461 $res .= "}";
462 672
463 $also = /\G\s*also\b/gc ? '$_, ' : ''; 673 if (/\Gof\s+(self|object|source|originator)\b/gc) {
464 last unless /\G\s*in\b/gc; 674 $also || $deep
675 and die "neither 'also' nor 'deep' can be used with 'of'\n";
676
677 if ($1 eq "self") {
678 return "$res \$self // ()";
679 } elsif ($1 eq "object") {
680 return "$res \$object";
681 } elsif ($1 eq "source") {
682 return "$res \$source // ()";
683 } elsif ($1 eq "originator") {
684 return "$res \$originator // \$source // ()";
685 }
686 } else {
687 return "$res $defctx";
688 }
465 } 689 }
466 } 690 }
467
468 "$res \@ctx"
469 } 691 }
470
471} 692}
472 693
473sub parse($;$) { 694sub parse($$) { # wantarray, matchexpr
474 local $_ = shift; 695 my $res;
475 local $all = shift;
476 696
477 my $res = "package cf::match::exec;\n" 697 local $_ = $_[1];
478 . eval { cf::match::parser::select }; 698
699 eval {
700 $res = cf::match::parser::match $_[0], "\$object";
701
702 /\G$/gc
703 or die "unexpected trailing characters after match\n";
704 };
479 705
480 if ($@) { 706 if ($@) {
481 my $ctx = 20; 707 my $ctx = 20;
482 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 708 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
483 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 709 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
487 } 713 }
488 714
489 $res 715 $res
490} 716}
491 717
492if (0) { 718if (0) {#d#
493 my $perl = parse '{ {1}}', 0; 719 die parse 1, 'stats.pow';
494
495 warn $perl, "\n";#d#
496 $perl = eval "no warnings; no feature; sub { $perl }"; die if $@;
497 use B::Deparse;
498 warn B::Deparse->new->coderef2text ($perl);
499 exit 0; 720 exit 0;
500} 721}
501 722
723our %CACHE;
724
725sub compile($$) {
726 my ($wantarray, $match) = @_;
727 my $expr = parse $wantarray, $match;
728 warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
729 $expr = eval "
730 package cf::match::exec;
731 sub {
732 my (\$object, \$self, \$source, \$originator) = \@_;
733 $expr
734 }
735 ";
736 die if $@;
737
738 $expr
739}
740
741=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
742
743Compiles (and caches) the C<$match> expression and matches it against
744the C<$object>. C<$self> should be the object initiating the match (or
745C<undef>), C<$source> should be the actor/source and C<$originator> the
746object that initiated the action (such as the player). C<$originator>
747defaults to C<$source> when not given.
748
749In list context it finds and returns all matching objects, in scalar
750context only a true or false value.
751
752=cut
753
754sub match($$;$$$) {
755 my $match = shift;
756 my $wantarray = wantarray+0;
757
758 &{
759 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
760 }
761}
762
763our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
764 %CACHE = ();
765};
766
767#d# $::schmorp=cf::player::find "schmorp"&
768#d# cf::match::match '', $::schmorp->ob
769
770
771=back
772
773=head1 AUTHOR
774
775 Marc Lehmann <schmorp@schmorp.de>
776 http://home.schmorp.de/
777
778=cut
779
5021; 7801;
503 781

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines