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.10 by root, Sun Oct 11 05:31:55 2009 UTC vs.
Revision 1.22 by root, Sat Oct 24 11:45:40 2009 UTC

25 25
26Find all potions with spell objects inside them in someones inventory: 26Find all potions with spell objects inside them in someones inventory:
27 27
28 type=SPELL in type=POTION in inv 28 type=SPELL in type=POTION in inv
29 29
30Find all potions inside someones inventory, or inside applied containers: 30Find all scrolls inside someones inventory, or inside applied scroll
31containers:
31 32
32 type=POTION also in type=CONTAINER and applied in inv 33 type=SCROLL also in applied type=CONTAINER race="scroll" in inv
34
35Find all unpaid items, anywhere, even deeply nested inside other items, in
36the originator:
37
38 unpaid also deep in inv of originator
33 39
34=head1 MATCH EXPRESSIONS 40=head1 MATCH EXPRESSIONS
35 41
36=head2 STRUCTURE 42=head2 STRUCTURE
37 43
38The two main structures are the C<match>, which selects objects matching 44The two main structures are the C<match>, which selects objects matching
39various criteria, and the C<condition, which determines if an object 45various criteria, and the C<condition, which determines if an object
40matches some desired properties: 46matches some desired properties:
41 47
42 condition 48 condition
43 condition in set 49 condition in set-modifier
50 condition of root-object
44 51
45A C<cond> receives a set of "context objects" that it is applied to. This 52A 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, 53to. This is initially just one object - by default, for altars, it is the
47for pedestals, the object on top of it and so on. 54object dropped on it, for pedestals, the object on top of it and so on.
48 55
49This set of context objects can be modified in various ways, for example 56This 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 57by replacing it with the inventories of all objects, or all items on the
51same mapspace, and so on, by using the C<in> operator: 58same mapspace, and so on, by using the C<in> operator:
52 59
53 condition in inv 60 condition in inv
61 condition in map
62
63Also, besides the default root object where all this begins, you can start
64elsewhere, for example in the I<originator> (usually the player):
65
54 condition in inv in originator 66 condition in inv of originator
55 67
56Once the set of context objects has been established, each object is 68Once the final set of context objects has been established, each object
57matched against the C<cond> expression. Sometimes the server is only 69is matched against the C<condition>.
58interested in knowing whether I<anything> matches, and sometimes the 70
59server is interested in I<all> objects that match. 71It is possible to chain modifiers from right-to-left, so this example
72would start with the originator, take it's inventory, find all inventory
73items which are potions, looks into their inventory, and then finds all
74spells.
75
76 type=SPELL in type=POTION in inv of originator
77
78Sometimes the server is only interested in knowing whether I<anything>
79matches, and sometimes the server is interested in I<all> objects that
80match.
60 81
61=head2 OPERATORS 82=head2 OPERATORS
62 83
63=over 4 84=over 4
64 85
65=item and, or, not, () 86=item and, or, not, ()
66 87
67Conditions can be combined with C<and> or C<or> to build larger 88Conditions can be combined with C<and> or C<or> to build larger
68expressions. C<not> negates the expression, and parentheses can be used to 89expressions. C<not> negates the condition, and parentheses can be used to
69group conditions. 90override operator precedence and execute submatches.
91
92Not that C<not> only negates a condition and not the whole match
93expressions, thus
94
95 not applied in inv
96
97is true if there is I<any> non-object in the inventory. To negate a whole
98match, you have to use a sub-match. To check whether there is I<no>
99applied object in someones inventory, write this:
100
101 not (applied in inv)
70 102
71Example: match applied weapons. 103Example: match applied weapons.
72 104
73 type=WEAPON and applied 105 applied type=WEAPON
74 106
75Example: match horns or rods. 107Example: match horns or rods.
76 108
77 type=HORN or type=ROD 109 type=HORN or type=ROD
78 110
84the match, the C<in> operator lets you look at other sets of objects, most 116the match, the C<in> operator lets you look at other sets of objects, most
85often the inventory. 117often the inventory.
86 118
87=over 4 119=over 4
88 120
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 121=item in inv
121 122
122Replaces all objects by their inventory. 123Replaces all objects by their inventory.
123 124
124Example: find all spell objects inside the object to be matched. 125Example: find all spell objects inside the object to be matched.
127 128
128=item in env 129=item in env
129 130
130Replaces all objects by their containing object, if they have one. 131Replaces all objects by their containing object, if they have one.
131 132
133=item in arch
134
135Replaces all objects by their archetypes.
136
132=item in map 137=item in map
133 138
134Replaces all objects by the objects that are on the same mapspace as them. 139Replaces all objects by the objects that are on the same mapspace as them.
135 140
141=item in head
142
143Replaces all objects by their head objects.
144
136=item in <cond> 145=item in <condition>
137 146
138Finds all context objects matching the condition, and then puts their 147Finds all context objects matching the condition, and then puts their
139inventories into the context set. 148inventories into the context set.
140 149
141Note that C<in inv> is simply a special case of an C<< in <cond> >> that 150Note that C<in inv> is simply a special case of an C<< in <condition> >> that
142matches any object. 151matches any object.
143 152
144Example: find all spells inside potions inside the inventory of the context 153Example: find all spells inside potions inside the inventory of the context
145object(s). 154object(s).
146 155
153 162
154Example: check if the context object I<is> a spell, or I<contains> a spell. 163Example: check if the context object I<is> a spell, or I<contains> a spell.
155 164
156 type=SPELL also in inv 165 type=SPELL also in inv
157 166
158=item repeatedly in ... 167=item also deep in ...
159 168
160Repeats the operation as many times as possible. This can be used to 169Repeats the operation as many times as possible. This can be used to
161recursively look into objects. 170recursively look into objects.
162 171
163=item also repeatedly in ... 172So for example, C<also deep in inv> means to take the inventory of all
173objects, taking their inventories, and so on, and adding all these objects
174to the context set.
164 175
165C<also> and C<repeatedly> can be combined. 176Similarly, C<also deep in env> means to take the environment object, their
177environemnt object and so on.
166 178
167Example: check if there are any unpaid items in an inventory, 179Example: check if there are any unpaid items in an inventory,
168or in the inventories of the inventory objects, and so on. 180or in the inventories of the inventory objects, and so on.
169 181
170 unpaid also repeatedly in inv 182 unpaid also deep in inv
171 183
172Example: check if a object is inside a player. 184Example: check if a object is inside a player.
173 185
174 type=PLAYER also repeatedly in env 186 type=PLAYER also deep in env
175 187
176=back 188=back
189
190=item of ...
191
192By default, all matches are applied to the "obviously appropriate" object,
193such as the item dropped on a button or moving over a detector. This can
194be changed to a number of other objects - not all of them are available
195for each match (when not available, the match will simply fail).
196
197An C<of> term ends a match, nothing is allowed to follow.
198
199=over 4
200
201=item of object
202
203Starts with the default object - this is the object passed to the match to
204match against by default. Matches have an explicit C<of object> appended,
205but submatches start at the current object, and in this case C<of object>
206can be used to start at the original object once more.
207
208=item of source
209
210Starts with the I<source> object - this object is sometimes passed to
211matches and represents the object that is the source of the action, such
212as a rod or a potion when it is applied. Often, the I<source> is the same
213as the I<originator>.
214
215=item of originator
216
217Starts with the I<originator> - one step farther removed than the
218I<source>, the I<originator> is sometimes passed to matches and represents
219the original initiator of an action, most commonly a player or monster.
220
221This object is often identical to the I<source> (e.g. when a player casts
222a spell, the player is both source and originator).
223
224=item of self
225
226Starts with the object initiating/asking for the match - this is basically
227always the object that the match expression is attached to.
177 228
178=back 229=back
179 230
180=head2 EXPRESSIONS 231=head2 EXPRESSIONS
181 232
250=item any 301=item any
251 302
252This simply evaluates to true, and simply makes matching I<any> object a 303This simply evaluates to true, and simply makes matching I<any> object a
253bit easier to read. 304bit easier to read.
254 305
306=item none
307
308This simply evaluates to false, and simply makes matching I<never> a bit
309easier to read.
310
255=item has(cond) 311=item has(condition)
256 312
257True iff the object has a matching inventory object. 313True iff the object has a matching inventory object.
258 314
259=item count(match) 315=item count(match)
260 316
261Number of matching objects - the context object for the C<match> are the 317Number of matching objects - the context object for the C<match> is the
262original context objects for the overall C<match>. # TODO bullshit? 318currently tested object - you can override this with an C<in object> for
319example.
263 320
264=item match(match)
265
266An independent match - unlike C<count>, it only matters whether the match
267finds any object (which is faster).
268
269=item dump 321=item dump()
270 322
271Dumps the object to the server log when executed, and evaluates to true. 323Dumps the object to the server log when executed, and evaluates to true.
272 324
273Note that logical operations are short-circuiting, so this only dumps 325Note that logical operations are short-circuiting, so this only dumps
274potions: 326potions:
275 327
276 type=POTION and dump 328 type=POTION and dump()
277 329
278=back 330=back
279 331
280=head2 GRAMMAR 332=head2 GRAMMAR
281 333
283module. It is meant to be easily readable by humans, not to implement it 335module. It is meant to be easily readable by humans, not to implement it
284exactly as-is. 336exactly as-is.
285 337
286 # object matching and selecting 338 # object matching and selecting
287 339
288 match = set 340 match = chain
341 | chain 'of' root
342 root = 'object' | 'self' | 'source' | 'originator'
343 chain = condition
289 | match also rep 'in' set 344 | chain also deep 'in' modifier
290 also = nothing | 'also' 345 also = nothing | 'also'
291 rep = nothing | 'rep' | 'repeatedly' 346 deep = nothing | 'deep'
347 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
292 348
293 set = 'inv' | 'env' | 'map' 349 nothing =
294 | 'object' | 'source' | 'originator' | 'self'
295
296 empty =
297 350
298 # boolean matching condition 351 # boolean matching condition
299 352
300 condition = factor 353 condition = factor
301 | factor 'and'? cond 354 | factor 'and'? condition
302 | factor 'or' cond 355 | factor 'or' condition
303 356
304 factor = 'not' factor 357 factor = 'not' factor
305 | '(' cond ')' 358 | '(' match ')'
306 | expr 359 | expr
307 | expr operator constant 360 | expr operator constant
308 361
309 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 362 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
310 363
311 expr = flag 364 expr = flag
312 | sattr 365 | sattr
313 | aattr '[' <constant> ']' 366 | aattr '[' <constant> ']'
367 | 'stat.' statattr
314 | special 368 | special
315 | func '(' args ')' 369 | func '(' args ')'
316 | '{' perl code block '}' 370 | '{' perl code block '}'
317 371
318 func = <any function name> 372 func = <any function name>
319 sattr = <any scalar object attribute> 373 sattr = <any scalar object attribute>
320 aattr = <any array object attribute> 374 aattr = <any array object attribute>
321 flag = <any object flag> 375 flag = <any object flag>
376 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
322 special = <any ()-less "function"> 377 special = <any ()-less "function">
323 378
324 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 379 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
325 args = <depends on function> 380 args = <depends on function>
326 381
327 TODO: repeatedly, env, contains, possbly matches 382 TODO: contains, matches, query_name, selling_price, buying_price?
328 383
329=cut 384=cut
330 385
331=head2 PERL FUNCTIONS 386=head2 PERL FUNCTIONS
332 387
337package cf::match; 392package cf::match;
338 393
339use common::sense; 394use common::sense;
340 395
341use List::Util (); 396use List::Util ();
342
343# parser state
344# $_ # string to be parsed
345our $all; # find all, or just the first matching object
346 397
347{ 398{
348 package cf::match::exec; 399 package cf::match::exec;
349 400
350 use List::Util qw(first); 401 use List::Util qw(first);
351
352 sub env_chain {
353 my @res;
354 push @res, $_
355 while $_ = $_->env;
356 @res
357 }
358 402
359 package cf::match::parser; 403 package cf::match::parser;
360 404
361 use common::sense; 405 use common::sense;
362 406
363 sub ws { 407 sub ws {
364 /\G\s+/gc; 408 /\G\s+/gc;
365 } 409 }
366 410
411 sub condition ();
412 sub match ($$);
413
367 our %func = ( 414 our %func = (
368 has => sub { 415 has => sub {
369 'first { ' . &condition . ' } $_->inv' 416 'first { ' . condition . ' } $_->inv'
370 }, 417 },
371 count => sub { 418 count => sub {
372 local $all = 1;
373 '(scalar ' . &match . ')' 419 '(scalar ' . (match 1, '$_') . ')'
374 },
375 match => sub {
376 local $all = 0;
377 '(scalar ' . &match . ')'
378 },
379 );
380
381 our %special = (
382 any => sub {
383 1
384 }, 420 },
385 dump => sub { 421 dump => sub {
386 'do { 422 'do {
387 warn "cf::match::match dump:\n" 423 warn "cf::match::match dump:\n"
388 . "self: " . eval { $self->name } . "\n" 424 . "self: " . eval { $self->name } . "\n"
390 1 426 1
391 }'; 427 }';
392 }, 428 },
393 ); 429 );
394 430
431 our %special = (
432 any => sub {
433 1
434 },
435 none => sub {
436 0
437 },
438 );
439
395 sub constant { 440 sub constant {
396 ws; 441 ws;
397 442
398 return $1 if /\G([\-\+0-9\.]+)/gc; 443 return $1 if /\G([\-\+0-9\.]+)/gc;
399 return "cf::$1" if /\G([A-Z0-9_]+)/gc; 444 return "cf::$1" if /\G([A-Z0-9_]+)/gc;
405 } 450 }
406 451
407 our $flag = $cf::REFLECT{object}{flags}; 452 our $flag = $cf::REFLECT{object}{flags};
408 our $sattr = $cf::REFLECT{object}{scalars}; 453 our $sattr = $cf::REFLECT{object}{scalars};
409 our $aattr = $cf::REFLECT{object}{arrays}; 454 our $aattr = $cf::REFLECT{object}{arrays};
455 our $lattr = $cf::REFLECT{living}{scalars};
410 456
411 sub expr { 457 sub expr {
412 # ws done by factor 458 # ws done by factor
413 my $res; 459 my $res;
414 460
417 463
418 my $expr = $1; 464 my $expr = $1;
419 465
420 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 466 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
421 467
468 } elsif (/\Gstats\.([A-Za-z0-9_]+)/gc) {
469
470 if (exists $lattr->{$1}) {
471 $res .= "\$_->stats->$1";
472 } elsif (exists $lattr->{"\u$1"}) {
473 $res .= "\$_->stats->\u$1";
474 } else {
475 die "living statistic name expected (str, pow, hp, sp...)\n";
476 }
477
422 } elsif (/\G([A-Za-z0-9_]+)/gc) { 478 } elsif (/\G([A-Za-z0-9_]+)/gc) {
423 479
424 if (my $func = $func{$1}) { 480 if (my $func = $func{$1}) {
425 /\G\s*\(/gc 481 /\G\s*\(/gc
426 or die "'(' expected after function name\n"; 482 or die "'(' expected after function name\n";
454 } else { 510 } else {
455 $res .= constant; 511 $res .= constant;
456 } 512 }
457 513
458 } else { 514 } else {
515 Carp::cluck;#d#
459 die "expr expected\n"; 516 die "expr expected\n";
460 } 517 }
461 518
462 $res 519 $res
463 } 520 }
480 $res .= "!"; 537 $res .= "!";
481 } 538 }
482 539
483 if (/\G\(/gc) { 540 if (/\G\(/gc) {
484 # () 541 # ()
485 $res .= &condition; 542
486 ws; 543 $res .= '(' . (match 0, '$_') . ')';
544
487 /\G\)/gc or die "')' expected\n"; 545 /\G\s*\)/gc or die "closing ')' expected\n";
488 546
489 } else { 547 } else {
490 my $expr = expr; 548 my $expr = expr;
491 549
492 $res .= $expr; 550 $res .= $expr;
503 } 561 }
504 562
505 "($res)" 563 "($res)"
506 } 564 }
507 565
508 sub condition { 566 sub condition () {
509 my $res = factor; 567 my $res = factor;
510 568
511 while () { 569 while () {
512 ws; 570 ws;
571
572 # first check some stop-symbols, so we don't have to backtrack
513 if (/\G(?=also\b|in\b|\)|$)/gc) { 573 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
514 # early stop => faster and requires no backtracking 574 pos = pos; # argh. the misop hits again. again. again. again. you die.
515 last; 575 last;
576
516 } elsif (/\Gor\b/gc) { 577 } elsif (/\Gor\b/gc) {
517 $res .= " || "; 578 $res .= " || ";
579
518 } else { 580 } else {
519 /\Gand\b/gc; 581 /\Gand\b/gc;
520 $res .= " && "; 582 $res .= " && ";
521 } 583 }
522 $res .= factor; 584 $res .= factor;
523 } 585 }
524 586
525 $res 587 $res
526 } 588 }
527 589
528 sub match { 590 sub match ($$) {
529 my $res; 591 my ($wantarray, $defctx) = @_;
530 592
531 my $also; # undef means first iteration 593 my $res = condition;
594
595 # if nothing follows, we have a simple condition, so
596 # optimise a comon case.
597 if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) {
598 return $wantarray
599 ? "$res ? \$_ : ()"
600 : $res;
601 }
602
603 $res = ($wantarray ? " grep { " : " first { ") . $res . "}";
604
532 while () { 605 while () {
533 if (/\G\s*(inv|env|map|object|subject|originator)\b/gc) { 606 ws;
607
608 my $also = /\Galso\s+/gc + 0;
609 my $deep = /\Gdeep\s+/gc + 0;
610
611 if (/\Gin\s+/gc) {
612 my $expand;
613
614 if (/\G(inv|env|map|arch|head)\b/gc) {
534 if ($1 eq "inv") { 615 if ($1 eq "inv") {
535 $res .= " map+(${also}\$_->inv),"; 616 $expand = "map \$_->inv,";
536 } elsif ($1 eq "env") { 617 } elsif ($1 eq "env") {
537 $res .= " map+(${also}env_chain), "; # TODO 618 $expand = "map \$_->env // (),";
619 } elsif ($1 eq "head") {
620 $expand = "map \$_->head,";
621 $deep = 0; # infinite loop otherwise
622 } elsif ($1 eq "arch") {
623 $expand = "map \$_->arch,";
624 $deep = 0; # infinite loop otherwise
538 } elsif ($1 eq "map") { 625 } elsif ($1 eq "map") {
539 $res .= " map+(${also}\$_->map->at (\$_->x, \$_->y)),"; 626 $expand = "map \$_->map->at (\$_->x, \$_->y),";
540 } elsif ($1 eq "self") { 627 $deep = 0; # infinite loop otherwise
541 return "$res \$self"; 628 }
542 } elsif ($1 eq "object") { 629 } else {
543 last; # default 630 $expand = "map \$_->inv, grep { " . condition . " }";
544 } elsif ($1 eq "source") {
545 return "$res \$source";
546 } elsif ($1 eq "originator") {
547 return "$res \$originator";
548 } 631 }
549 last unless /\G\s*in\b/gc; 632
633 if ($also || $deep) {
634 $res .= " do {\n"
635 . " my \@res;\n";
636 $res .= " while (\@_) {\n" if $deep;
637 $res .= " push \@res, \@_;\n" if $also;
638 $res .= " \@_ = $expand \@_;\n";
639 $res .= " }\n" if $deep;
640 $res .= " (\@res, \@_)\n"
641 . "}";
642 } else {
643 $res .= " $expand";
644 }
550 } else { 645 } else {
551 $res .= " map+($also\$_->inv)," if defined $also;
552 $res .= $all ? " grep { " : " first {";
553 $res .= condition;
554 $res .= "}";
555 646
556 $also = /\G\s*also\b/gc ? '$_, ' : ''; 647 if (/\Gof\s+(self|object|source|originator)\b/gc) {
557 last unless /\G\s*in\b/gc; 648 $also || $deep
649 and die "neither 'also' nor 'deep' can be used with 'of'\n";
650
651 if ($1 eq "self") {
652 return "$res \$self // ()";
653 } elsif ($1 eq "object") {
654 return "$res \$object";
655 } elsif ($1 eq "source") {
656 return "$res \$source // ()";
657 } elsif ($1 eq "originator") {
658 return "$res \$originator // \$source // ()";
659 }
660 } else {
661 return "$res $defctx";
662 }
558 } 663 }
559 } 664 }
560
561 "$res \$object"
562 } 665 }
563
564} 666}
565 667
566sub parse($;$) { 668sub parse($$) { # wantarray, matchexpr
567 local $_ = shift; 669 my $res;
568 local $all = shift;
569 670
671 local $_ = $_[1];
672
673 eval {
570 my $res = cf::match::parser::match; 674 $res = cf::match::parser::match $_[0], "\$object";
675
676 /\G$/gc
677 or die "unexpected trailing characters after match\n";
678 };
571 679
572 if ($@) { 680 if ($@) {
573 my $ctx = 20; 681 my $ctx = 20;
574 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 682 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
575 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 683 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
577 chomp $@; 685 chomp $@;
578 die "$@ ($str)\n"; 686 die "$@ ($str)\n";
579 } 687 }
580 688
581 $res 689 $res
690}
691
692if (0) {#d#
693 die parse 1, 'stats.pow';
694 exit 0;
695}
696
697our %CACHE;
698
699sub compile($$) {
700 my ($wantarray, $match) = @_;
701 my $expr = parse $wantarray, $match;
702 warn "MATCH DEBUG $match,$wantarray => $expr\n";#d#
703 $expr = eval "
704 package cf::match::exec;
705 sub {
706 my (\$object, \$self, \$source, \$originator) = \@_;
707 $expr
708 }
709 ";
710 die if $@;
711
712 $expr
582} 713}
583 714
584=item cf::match::match $match, $object[, $self[, $source[, $originator]]] 715=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
585 716
586Compiles (and caches) the C<$match> expression and matches it against 717Compiles (and caches) the C<$match> expression and matches it against
592In list context it finds and returns all matching objects, in scalar 723In list context it finds and returns all matching objects, in scalar
593context only a true or false value. 724context only a true or false value.
594 725
595=cut 726=cut
596 727
597our %CACHE;
598
599sub compile($$) {
600 my ($match, $all) = @_;
601 my $expr = parse $match, $all;
602 warn "$match,$all => $expr\n";#d#
603 $expr = eval "
604 package cf::match::exec;
605 sub {
606 my (\$object, \$self, \$source, \$originator) = \@_;
607 \$originator ||= \$source;
608 $expr
609 }
610 ";
611 die if $@;
612
613 $expr
614}
615
616sub match($$;$$$) { 728sub match($$;$$$) {
617 my $match = shift; 729 my $match = shift;
618 my $all = wantarray+0; 730 my $wantarray = wantarray+0;
619 731
620 &{ 732 &{
621 $CACHE{"$all$match"} ||= compile $match, $all 733 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
622 } 734 }
623} 735}
624 736
737our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
738 %CACHE = ();
739};
740
625#d# $::schmorp=cf::player::find "schmorp"& 741#d# $::schmorp=cf::player::find "schmorp"&
626#d# cf::match::match '', $::schmorp->ob 742#d# cf::match::match '', $::schmorp->ob
627 743
628 744
629=back 745=back

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines