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.11 by root, Sun Oct 11 18:18:03 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
25 47
26Find all potions with spell objects inside them in someones inventory: 48Find all potions with spell objects inside them in someones inventory:
27 49
28 type=SPELL in type=POTION in inv 50 type=SPELL in type=POTION in inv
29 51
30Find all potions inside someones inventory, or inside applied containers: 52Find all scrolls inside someones inventory, or inside applied scroll
53containers:
31 54
32 type=POTION also in type=CONTAINER and applied in inv 55 type=SCROLL also in applied type=CONTAINER race="scroll" in inv
56
57Find all unpaid items, anywhere, even deeply nested inside other items, in
58the originator:
59
60 unpaid also deep in inv of originator
33 61
34=head1 MATCH EXPRESSIONS 62=head1 MATCH EXPRESSIONS
35 63
36=head2 STRUCTURE 64=head2 STRUCTURE
37 65
38The two main structures are the C<match>, which selects objects matching 66The two main structures are the C<match>, which selects objects matching
39various criteria, and the C<condition, which determines if an object 67various criteria, and the C<condition, which determines if an object
40matches some desired properties: 68matches some desired properties:
41 69
42 condition 70 condition
43 condition in set 71 condition in set-modifier
72 condition of root-object
44 73
45A C<cond> receives a set of "context objects" that it is applied to. This 74A C<condition> receives a set of "context objects" that it is applied
46is initially just one object - for altars, it is the object dropped on it, 75to. This is initially just one object - by default, for altars, it is the
47for 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.
48 77
49This 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
50by 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
51same mapspace, and so on, by using the C<in> operator: 80same mapspace, and so on, by using the C<in> operator:
52 81
53 condition in inv 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
54 condition in inv in originator 88 condition in inv of originator
55 89
56Once the set of context objects has been established, each object is 90Once the final set of context objects has been established, each object
57matched against the C<cond> expression. Sometimes the server is only 91is matched against the C<condition>.
58interested in knowing whether I<anything> matches, and sometimes the 92
59server 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.
60 103
61=head2 OPERATORS 104=head2 OPERATORS
62 105
63=over 4 106=over 4
64 107
65=item and, or, not, () 108=item and, or, not, ()
66 109
67Conditions 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
68expressions. C<not> negates the expression, and parentheses can be used to 111expressions. C<not> negates the condition, and parentheses can be used to
69group 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)
70 124
71Example: match applied weapons. 125Example: match applied weapons.
72 126
73 type=WEAPON and applied 127 applied type=WEAPON
74 128
75Example: match horns or rods. 129Example: match horns or rods.
76 130
77 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
78 136
79=item in ... 137=item in ...
80 138
81The 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
82a 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>
84the match, the C<in> operator lets you look at other sets of objects, most 142the match, the C<in> operator lets you look at other sets of objects, most
85often the inventory. 143often the inventory.
86 144
87=over 4 145=over 4
88 146
89=item in object
90
91Replaces all objects by the default object - this is the object passed to
92the match to match against by default. All matches have an explicit C<in
93ctx> appended.
94
95This must be the last C<in> expression in a match.
96
97=item in source
98
99Replaces all objects by the source object - this object is sometimes
100passed to matches and represents the object is the source of the action,
101such as a rod or a potion when it is applied. Often, the I<source> is the
102same as the I<originator>.
103
104This must be the last C<in> expression in a match.
105
106=item in originator
107
108Replaces all objects by the originator object - one step farther removed
109than the I<source>, the I<originator> is sometimes passed to matches and
110represents the original initiator of an action, most commonly a player or
111monster.
112
113=item in self
114
115Replaces all objects by the object initiating/asking for the match - this
116is basically always the object thatt he match expression is attached to.
117
118This must be the last C<in> expression in a match.
119
120=item in inv 147=item in inv
121 148
122Replaces all objects by their inventory. 149Replaces all objects by their inventory.
123 150
124Example: find all spell objects inside the object to be matched. 151Example: find all spell objects inside the object to be matched.
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
167=item in head
168
169Replaces all objects by their head objects.
170
136=item in <cond> 171=item in <condition>
137 172
138Finds all context objects matching the condition, and then puts their 173Finds all context objects matching the condition, and then puts their
139inventories 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 <cond> >> 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
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.
177 254
178=back 255=back
179 256
180=head2 EXPRESSIONS 257=head2 EXPRESSIONS
181 258
250=item any 327=item any
251 328
252This 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
253bit easier to read. 330bit easier to read.
254 331
332=item none
333
334This simply evaluates to false, and simply makes matching I<never> a bit
335easier to read.
336
255=item has(condition) 337=item has(condition)
256 338
257True iff the object has a matching inventory object. 339True iff the object has a matching inventory object.
258 340
259=item count(match) 341=item count(match)
260 342
261Number of matching objects - the context object for the C<match> is the 343Number of matching objects - the context object for the C<match> is the
262currently tested object - you can override this with an C<in object> for 344currently tested object - you can override this with an C<in object> for
263example. 345example.
264 346
265=item match(match)
266
267An independent match - semantics like C<count>, except it only matters
268whether the match finds any object (which is faster).
269
270=item dump 347=item dump()
271 348
272Dumps the object to the server log when executed, and evaluates to true. 349Dumps the object to the server log when executed, and evaluates to true.
273 350
274Note that logical operations are short-circuiting, so this only dumps 351Note that logical operations are short-circuiting, so this only dumps
275potions: 352potions:
276 353
277 type=POTION and dump 354 type=POTION and dump()
278 355
279=back 356=back
280 357
281=head2 GRAMMAR 358=head2 GRAMMAR
282 359
284module. It is meant to be easily readable by humans, not to implement it 361module. It is meant to be easily readable by humans, not to implement it
285exactly as-is. 362exactly as-is.
286 363
287 # object matching and selecting 364 # object matching and selecting
288 365
289 match = set 366 match = chain
367 | chain 'of' root
368 root = 'object' | 'self' | 'source' | 'originator'
369 chain = condition
290 | match also rep 'in' set 370 | chain also deep 'in' modifier
291 also = nothing | 'also' 371 also = nothing | 'also'
292 rep = nothing | 'rep' | 'repeatedly' 372 deep = nothing | 'deep'
373 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
293 374
294 set = 'inv' | 'env' | 'map' 375 nothing =
295 | 'object' | 'source' | 'originator' | 'self'
296
297 empty =
298 376
299 # boolean matching condition 377 # boolean matching condition
300 378
301 condition = factor 379 condition = factor
302 | factor 'and'? cond 380 | factor 'and'? condition
303 | factor 'or' cond 381 | factor 'or' condition
304 382
305 factor = 'not' factor 383 factor = 'not' factor
306 | '(' cond ')' 384 | '(' match ')'
307 | expr 385 | expr
308 | expr operator constant 386 | expr operator constant
309 387
310 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 388 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
311 389
312 expr = flag 390 expr = flag
313 | sattr 391 | sattr
314 | aattr '[' <constant> ']' 392 | aattr '[' <constant> ']'
393 | 'stat.' statattr
315 | special 394 | special
316 | func '(' args ')' 395 | func '(' args ')'
317 | '{' perl code block '}' 396 | '{' perl code block '}'
318 397
319 func = <any function name> 398 func = <any function name>
320 sattr = <any scalar object attribute> 399 sattr = <any scalar object attribute>
321 aattr = <any array object attribute> 400 aattr = <any array object attribute>
322 flag = <any object flag> 401 flag = <any object flag>
402 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
323 special = <any ()-less "function"> 403 special = <any ()-less "function">
324 404
325 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 405 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
326 args = <depends on function> 406 args = <depends on function>
327 407
328 TODO: repeatedly, env, contains, possbly matches 408 TODO: contains, matches, query_name, selling_price, buying_price?
329 409
330=cut 410=cut
331 411
332=head2 PERL FUNCTIONS 412=head2 PERL FUNCTIONS
333 413
338package cf::match; 418package cf::match;
339 419
340use common::sense; 420use common::sense;
341 421
342use List::Util (); 422use List::Util ();
343
344# parser state
345# $_ # string to be parsed
346our $all; # find all, or just the first matching object
347 423
348{ 424{
349 package cf::match::exec; 425 package cf::match::exec;
350 426
351 use List::Util qw(first); 427 use List::Util qw(first);
352
353 sub env_chain {
354 my @res;
355 push @res, $_
356 while $_ = $_->env;
357 @res
358 }
359 428
360 package cf::match::parser; 429 package cf::match::parser;
361 430
362 use common::sense; 431 use common::sense;
363 432
364 sub ws { 433 sub ws {
365 /\G\s+/gc; 434 /\G\s+/gc;
366 } 435 }
367 436
437 sub condition ();
438 sub match ($$);
439
368 our %func = ( 440 our %func = (
369 has => sub { 441 has => sub {
370 'first { ' . &condition . ' } $_->inv' 442 'first { ' . condition . ' } $_->inv'
371 }, 443 },
372 count => sub { 444 count => sub {
373 local $all = 1;
374 '(scalar ' . &match ('$_') . ')' 445 '(scalar ' . (match 1, '$_') . ')'
375 },
376 match => sub {
377 local $all = 0;
378 '(scalar ' . &match ('$_') . ')'
379 },
380 );
381
382 our %special = (
383 any => sub {
384 1
385 }, 446 },
386 dump => sub { 447 dump => sub {
387 'do { 448 'do {
388 warn "cf::match::match dump:\n" 449 warn "cf::match::match dump:\n"
389 . "self: " . eval { $self->name } . "\n" 450 . "self: " . eval { $self->name } . "\n"
391 1 452 1
392 }'; 453 }';
393 }, 454 },
394 ); 455 );
395 456
457 our %special = (
458 any => sub {
459 1
460 },
461 none => sub {
462 0
463 },
464 );
465
396 sub constant { 466 sub constant {
397 ws; 467 ws;
398 468
399 return $1 if /\G([\-\+0-9\.]+)/gc; 469 return $1 if /\G([\-\+0-9\.]+)/gc;
400 return "cf::$1" if /\G([A-Z0-9_]+)/gc; 470 return "cf::$1" if /\G([A-Z0-9_]+)/gc;
406 } 476 }
407 477
408 our $flag = $cf::REFLECT{object}{flags}; 478 our $flag = $cf::REFLECT{object}{flags};
409 our $sattr = $cf::REFLECT{object}{scalars}; 479 our $sattr = $cf::REFLECT{object}{scalars};
410 our $aattr = $cf::REFLECT{object}{arrays}; 480 our $aattr = $cf::REFLECT{object}{arrays};
481 our $lattr = $cf::REFLECT{living}{scalars};
411 482
412 sub expr { 483 sub expr {
413 # ws done by factor 484 # ws done by factor
414 my $res; 485 my $res;
415 486
418 489
419 my $expr = $1; 490 my $expr = $1;
420 491
421 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 492 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
422 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
423 } elsif (/\G([A-Za-z0-9_]+)/gc) { 504 } elsif (/\G([A-Za-z0-9_]+)/gc) {
424 505
425 if (my $func = $func{$1}) { 506 if (my $func = $func{$1}) {
426 /\G\s*\(/gc 507 /\G\s*\(/gc
427 or die "'(' expected after function name\n"; 508 or die "'(' expected after function name\n";
455 } else { 536 } else {
456 $res .= constant; 537 $res .= constant;
457 } 538 }
458 539
459 } else { 540 } else {
541 Carp::cluck;#d#
460 die "expr expected\n"; 542 die "expr expected\n";
461 } 543 }
462 544
463 $res 545 $res
464 } 546 }
481 $res .= "!"; 563 $res .= "!";
482 } 564 }
483 565
484 if (/\G\(/gc) { 566 if (/\G\(/gc) {
485 # () 567 # ()
486 $res .= &condition; 568
487 ws; 569 $res .= '(' . (match 0, '$_') . ')';
570
488 /\G\)/gc or die "')' expected\n"; 571 /\G\s*\)/gc or die "closing ')' expected\n";
489 572
490 } else { 573 } else {
491 my $expr = expr; 574 my $expr = expr;
492 575
493 $res .= $expr; 576 $res .= $expr;
504 } 587 }
505 588
506 "($res)" 589 "($res)"
507 } 590 }
508 591
509 sub condition { 592 sub condition () {
510 my $res = factor; 593 my $res = factor;
511 594
512 while () { 595 while () {
513 ws; 596 ws;
597
598 # first check some stop-symbols, so we don't have to backtrack
514 if (/\G(?=also\b|in\b|\)|$)/gc) { 599 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
515 # early stop => faster and requires no backtracking 600 pos = pos; # argh. the misop hits again. again. again. again. you die.
516 last; 601 last;
602
517 } elsif (/\Gor\b/gc) { 603 } elsif (/\Gor\b/gc) {
518 $res .= " || "; 604 $res .= " || ";
605
519 } else { 606 } else {
520 /\Gand\b/gc; 607 /\Gand\b/gc;
521 $res .= " && "; 608 $res .= " && ";
522 } 609 }
523 $res .= factor; 610 $res .= factor;
524 } 611 }
525 612
526 $res 613 $res
527 } 614 }
528 615
529 sub match { 616 sub match ($$) {
530 my $default = shift; 617 my ($wantarray, $defctx) = @_;
531 618
532 my $res; 619 my $res = condition;
533 620
534 my $also; # undef means first iteration 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
535 while () { 631 while () {
536 if (/\G\s*(inv|env|map|object|subject|originator)\b/gc) { 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
640 if (/\G(inv|env|map|arch|head)\b/gc) {
537 if ($1 eq "inv") { 641 if ($1 eq "inv") {
538 $res .= " map+(${also}\$_->inv),"; 642 $expand = "map \$_->inv,";
539 } elsif ($1 eq "env") { 643 } elsif ($1 eq "env") {
540 $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
541 } elsif ($1 eq "map") { 651 } elsif ($1 eq "map") {
542 $res .= " map+(${also}\$_->map->at (\$_->x, \$_->y)),"; 652 $expand = "map \$_->map->at (\$_->x, \$_->y),";
543 } elsif ($1 eq "self") { 653 $deep = 0; # infinite loop otherwise
544 return "$res \$self"; 654 }
545 } elsif ($1 eq "object") { 655 } else {
546 return "$res \$object"; 656 $expand = "map \$_->inv, grep { " . condition . " }";
547 } elsif ($1 eq "source") {
548 return "$res \$source";
549 } elsif ($1 eq "originator") {
550 return "$res \$originator";
551 } 657 }
552 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 }
553 } else { 671 } else {
554 $res .= " map+($also\$_->inv)," if defined $also;
555 $res .= $all ? " grep { " : " first {";
556 $res .= condition;
557 $res .= "}";
558 672
559 $also = /\G\s*also\b/gc ? '$_, ' : ''; 673 if (/\Gof\s+(self|object|source|originator)\b/gc) {
560 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 }
561 } 689 }
562 } 690 }
563
564 "$res $default"
565 } 691 }
566
567} 692}
568 693
569sub parse($;$) { 694sub parse($$) { # wantarray, matchexpr
570 local $_ = shift; 695 my $res;
571 local $all = shift;
572 696
697 local $_ = $_[1];
698
699 eval {
573 my $res = cf::match::parser::match "\$object"; 700 $res = cf::match::parser::match $_[0], "\$object";
701
702 /\G$/gc
703 or die "unexpected trailing characters after match\n";
704 };
574 705
575 if ($@) { 706 if ($@) {
576 my $ctx = 20; 707 my $ctx = 20;
577 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 708 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
578 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 709 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
582 } 713 }
583 714
584 $res 715 $res
585} 716}
586 717
587if (0) { 718if (0) {#d#
588 die parse 'match(any in object)', 1; 719 die parse 1, 'stats.pow';
589 exit 0; 720 exit 0;
721}
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
590} 739}
591 740
592=item cf::match::match $match, $object[, $self[, $source[, $originator]]] 741=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
593 742
594Compiles (and caches) the C<$match> expression and matches it against 743Compiles (and caches) the C<$match> expression and matches it against
600In list context it finds and returns all matching objects, in scalar 749In list context it finds and returns all matching objects, in scalar
601context only a true or false value. 750context only a true or false value.
602 751
603=cut 752=cut
604 753
605our %CACHE;
606
607sub compile($$) {
608 my ($match, $all) = @_;
609 my $expr = parse $match, $all;
610 warn "$match,$all => $expr\n";#d#
611 $expr = eval "
612 package cf::match::exec;
613 sub {
614 my (\$object, \$self, \$source, \$originator) = \@_;
615 \$originator ||= \$source;
616 $expr
617 }
618 ";
619 die if $@;
620
621 $expr
622}
623
624sub match($$;$$$) { 754sub match($$;$$$) {
625 my $match = shift; 755 my $match = shift;
626 my $all = wantarray+0; 756 my $wantarray = wantarray+0;
627 757
628 &{ 758 &{
629 $CACHE{"$all$match"} ||= compile $match, $all 759 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
630 } 760 }
631} 761}
632 762
763our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
764 %CACHE = ();
765};
766
633#d# $::schmorp=cf::player::find "schmorp"& 767#d# $::schmorp=cf::player::find "schmorp"&
634#d# cf::match::match '', $::schmorp->ob 768#d# cf::match::match '', $::schmorp->ob
635 769
636 770
637=back 771=back

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines