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.7 by root, Sat Oct 10 05:39:48 2009 UTC vs.
Revision 1.28 by root, Sat Aug 28 00:05:33 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
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 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
239=item of source
240
241Starts with the I<source> object - this object is sometimes passed to
242matches and represents the object that is the source of the action, such
243as a rod or a potion when it is applied. Often, the I<source> is the same
244as the I<originator>.
245
246=item of originator
247
248Starts with the I<originator> - one step farther removed than the
249I<source>, the I<originator> is sometimes passed to matches and represents
250the original initiator of an action, most commonly a player or monster.
251
252This object is often identical to the I<source> (e.g. when a player casts
253a spell, the player is both source and originator).
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 use List::Util qw(first); 427 use List::Util qw(first);
276
277 sub env_chain {
278 my @res;
279 push @res, $_
280 while $_ = $_->env;
281 @res
282 }
283 428
284 package cf::match::parser; 429 package cf::match::parser;
285 430
286 use common::sense; 431 use common::sense;
287 432
288 sub ws { 433 sub ws {
289 /\G\s+/gc; 434 /\G\s+/gc;
290 } 435 }
291 436
437 sub condition ();
438 sub match ($$);
439
292 our %func = ( 440 our %func = (
293 has => sub { 441 has => sub {
294 'first { ' . &match . ' } $_->inv' 442 'first { ' . condition . ' } $_->inv'
295 }, 443 },
296 count => sub { 444 count => sub {
297 local $all = 1; 445 '(scalar ' . (match 1, '$_') . ')'
298 '(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 }';
299 }, 454 },
300 ); 455 );
301 456
302 our %special = ( 457 our %special = (
303 any => sub { 458 any => sub {
304 1 459 1
305 }, 460 },
461 none => sub {
462 0
463 },
306 ); 464 );
307 465
308 sub constant { 466 sub constant {
309 ws; 467 ws;
310 468
317 die "number, string or uppercase constant name expected\n"; 475 die "number, string or uppercase constant name expected\n";
318 } 476 }
319 477
320 our $flag = $cf::REFLECT{object}{flags}; 478 our $flag = $cf::REFLECT{object}{flags};
321 our $sattr = $cf::REFLECT{object}{scalars}; 479 our $sattr = $cf::REFLECT{object}{scalars};
480 # quick hack to support archname, untested
481 $sattr->{archname} = "W";
322 our $aattr = $cf::REFLECT{object}{arrays}; 482 our $aattr = $cf::REFLECT{object}{arrays};
483 our $lattr = $cf::REFLECT{living}{scalars};
323 484
324 sub expr { 485 sub expr {
325 # ws done by factor 486 # ws done by factor
326 my $res; 487 my $res;
327 488
330 491
331 my $expr = $1; 492 my $expr = $1;
332 493
333 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 494 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
334 495
496 } elsif (/\Gstats\.([A-Za-z0-9_]+)/gc) {
497
498 if (exists $lattr->{$1}) {
499 $res .= "\$_->stats->$1";
500 } elsif (exists $lattr->{"\u$1"}) {
501 $res .= "\$_->stats->\u$1";
502 } else {
503 die "living statistic name expected (str, pow, hp, sp...)\n";
504 }
505
335 } elsif (/\G([A-Za-z0-9_]+)/gc) { 506 } elsif (/\G([A-Za-z0-9_]+)/gc) {
336 507
337 if (my $func = $func{$1}) { 508 if (my $func = $func{$1}) {
338 /\G\s*\(/gc 509 /\G\s*\(/gc
339 or die "'(' expected after function name\n"; 510 or die "'(' expected after function name\n";
367 } else { 538 } else {
368 $res .= constant; 539 $res .= constant;
369 } 540 }
370 541
371 } else { 542 } else {
543 Carp::cluck;#d#
372 die "expr expected\n"; 544 die "expr expected\n";
373 } 545 }
374 546
375 $res 547 $res
376 } 548 }
393 $res .= "!"; 565 $res .= "!";
394 } 566 }
395 567
396 if (/\G\(/gc) { 568 if (/\G\(/gc) {
397 # () 569 # ()
398 $res .= &match; 570
399 ws; 571 $res .= '(' . (match 0, '$_') . ')';
572
400 /\G\)/gc or die "')' expected\n"; 573 /\G\s*\)/gc or die "closing ')' expected\n";
401 574
402 } else { 575 } else {
403 my $expr = expr; 576 my $expr = expr;
404 577
405 $res .= $expr; 578 $res .= $expr;
416 } 589 }
417 590
418 "($res)" 591 "($res)"
419 } 592 }
420 593
421 sub match { 594 sub condition () {
422 my $res = factor; 595 my $res = factor;
423 596
424 while () { 597 while () {
425 ws; 598 ws;
599
600 # first check some stop-symbols, so we don't have to backtrack
426 if (/\G(?=also\b|in\b|\)|$)/gc) { 601 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
427 # early stop => faster and requires no backtracking 602 pos = pos; # argh. the misop hits again. again. again. again. you die.
428 last; 603 last;
604
429 } elsif (/\Gor\b/gc) { 605 } elsif (/\Gor\b/gc) {
430 $res .= " || "; 606 $res .= " || ";
607
431 } else { 608 } else {
432 /\Gand\b/gc; 609 /\Gand\b/gc;
433 $res .= " && "; 610 $res .= " && ";
434 } 611 }
435 $res .= factor; 612 $res .= factor;
436 } 613 }
437 614
438 $res 615 $res
439 } 616 }
440 617
441 sub select { 618 sub match ($$) {
442 my $res; 619 my ($wantarray, $defctx) = @_;
443 620
444 my $also; # undef means first iteration 621 my $res = condition;
622
623 # if nothing follows, we have a simple condition, so
624 # optimise a comon case.
625 if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) {
626 return $wantarray
627 ? "$res ? \$_ : ()"
628 : $res;
629 }
630
631 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
632
445 while () { 633 while () {
634 ws;
635
636 my $also = /\Galso\s+/gc + 0;
637 my $deep = /\Gdeep\s+/gc + 0;
638
639 if (/\Gin\s+/gc) {
640 my $expand;
641
446 if (/\G\s*(inv|env|map)\b/gc) { 642 if (/\G(inv|env|map|arch|head)\b/gc) {
447 if ($1 eq "inv") { 643 if ($1 eq "inv") {
448 $res .= " map+(${also}\$_->inv),"; 644 $expand = "map \$_->inv,";
449 } elsif ($1 eq "env") { 645 } elsif ($1 eq "env") {
450 $res .= " map+(${also}env_chain), "; # TODO 646 $expand = "map \$_->env // (),";
647 } elsif ($1 eq "head") {
648 $expand = "map \$_->head,";
649 $deep = 0; # infinite loop otherwise
650 } elsif ($1 eq "arch") {
651 $expand = "map \$_->arch,";
652 $deep = 0; # infinite loop otherwise
451 } elsif ($1 eq "map") { 653 } elsif ($1 eq "map") {
452 $res .= " map+(${also}\$_->map->at (\$_->x, \$_->y)),"; 654 $expand = "map \$_->map->at (\$_->x, \$_->y),";
655 $deep = 0; # infinite loop otherwise
656 }
657 } else {
658 $expand = "map \$_->inv, grep { " . condition . " }";
453 } 659 }
454 last unless /\G\s*in\b/gc; 660
661 if ($also || $deep) {
662 $res .= " do {\n"
663 . " my \@res;\n";
664 $res .= " while (\@_) {\n" if $deep;
665 $res .= " push \@res, \@_;\n" if $also;
666 $res .= " \@_ = $expand \@_;\n";
667 $res .= " }\n" if $deep;
668 $res .= " (\@res, \@_)\n"
669 . "}";
670 } else {
671 $res .= " $expand";
672 }
455 } else { 673 } else {
456 $res .= " map+($also\$_->inv)," if defined $also;
457 $res .= $all ? " grep { " : " first {";
458 $res .= match;
459 $res .= "}";
460 674
461 $also = /\G\s*also\b/gc ? '$_, ' : ''; 675 if (/\Gof\s+(self|object|source|originator)\b/gc) {
462 last unless /\G\s*in\b/gc; 676 $also || $deep
677 and die "neither 'also' nor 'deep' can be used with 'of'\n";
678
679 if ($1 eq "self") {
680 return "$res \$self // ()";
681 } elsif ($1 eq "object") {
682 return "$res \$object";
683 } elsif ($1 eq "source") {
684 return "$res \$source // ()";
685 } elsif ($1 eq "originator") {
686 return "$res \$originator // \$source // ()";
687 }
688 } else {
689 return "$res $defctx";
690 }
463 } 691 }
464 } 692 }
465
466 "$res \@ctx"
467 } 693 }
468
469} 694}
470 695
471sub parse($;$) { 696sub parse($$) { # wantarray, matchexpr
472 local $_ = shift; 697 my $res;
473 local $all = shift;
474 698
475 my $res = cf::match::parser::select; 699 local $_ = $_[1];
700
701 eval {
702 $res = cf::match::parser::match $_[0], "\$object";
703
704 /\G$/gc
705 or die "unexpected trailing characters after match\n";
706 };
476 707
477 if ($@) { 708 if ($@) {
478 my $ctx = 20; 709 my $ctx = 20;
479 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 710 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
480 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 711 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
484 } 715 }
485 716
486 $res 717 $res
487} 718}
488 719
489if (0) { 720if (0) {#d#
490 my $perl = parse '{ {1}}', 0; 721 die parse 1, 'stats.pow';
491
492 warn $perl, "\n";#d#
493 $perl = eval "package cf::match::exec; no warnings; no feature; our \@ctx; sub { $perl }"; die if $@;
494 use B::Deparse;
495 warn B::Deparse->new->coderef2text ($perl);
496 exit 0; 722 exit 0;
497} 723}
498 724
725our %CACHE;
726
727sub compile($$) {
728 my ($wantarray, $match) = @_;
729 my $expr = parse $wantarray, $match;
730# warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
731 $expr = eval "
732 package cf::match::exec;
733 sub {
734 my (\$object, \$self, \$source, \$originator) = \@_;
735 $expr
736 }
737 ";
738 die if $@;
739
740 $expr
741}
742
743=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
744
745Compiles (and caches) the C<$match> expression and matches it against
746the C<$object>. C<$self> should be the object initiating the match (or
747C<undef>), C<$source> should be the actor/source and C<$originator> the
748object that initiated the action (such as the player). C<$originator>
749defaults to C<$source> when not given.
750
751In list context it finds and returns all matching objects, in scalar
752context only a true or false value.
753
754=cut
755
756sub match($$;$$$) {
757 my $match = shift;
758 my $wantarray = wantarray+0;
759
760 &{
761 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
762 }
763}
764
765our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
766 %CACHE = ();
767};
768
769#d# $::schmorp=cf::player::find "schmorp"&
770#d# cf::match::match '', $::schmorp->ob
771
772
773=back
774
775=head1 AUTHOR
776
777 Marc Lehmann <schmorp@schmorp.de>
778 http://home.schmorp.de/
779
780=cut
781
4991; 7821;
500 783

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines