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.25 by root, Sat Mar 20 20:26:18 2010 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(cond) 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> are the 343Number of matching objects - the context object for the C<match> is the
262original context objects for the overall C<match>. # TODO bullshit? 344currently tested object - you can override this with an C<in object> for
345example.
263 346
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 347=item dump()
270 348
271Dumps 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.
272 350
273Note that logical operations are short-circuiting, so this only dumps 351Note that logical operations are short-circuiting, so this only dumps
274potions: 352potions:
275 353
276 type=POTION and dump 354 type=POTION and dump()
277 355
278=back 356=back
279 357
280=head2 GRAMMAR 358=head2 GRAMMAR
281 359
283module. 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
284exactly as-is. 362exactly as-is.
285 363
286 # object matching and selecting 364 # object matching and selecting
287 365
288 match = set 366 match = chain
367 | chain 'of' root
368 root = 'object' | 'self' | 'source' | 'originator'
369 chain = condition
289 | match also rep 'in' set 370 | chain also deep 'in' modifier
290 also = nothing | 'also' 371 also = nothing | 'also'
291 rep = nothing | 'rep' | 'repeatedly' 372 deep = nothing | 'deep'
373 modifier ='inv' | 'env' | 'arch' | 'map' | 'head'
292 374
293 set = 'inv' | 'env' | 'map' 375 nothing =
294 | 'object' | 'source' | 'originator' | 'self'
295
296 empty =
297 376
298 # boolean matching condition 377 # boolean matching condition
299 378
300 condition = factor 379 condition = factor
301 | factor 'and'? cond 380 | factor 'and'? condition
302 | factor 'or' cond 381 | factor 'or' condition
303 382
304 factor = 'not' factor 383 factor = 'not' factor
305 | '(' cond ')' 384 | '(' match ')'
306 | expr 385 | expr
307 | expr operator constant 386 | expr operator constant
308 387
309 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 388 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
310 389
311 expr = flag 390 expr = flag
312 | sattr 391 | sattr
313 | aattr '[' <constant> ']' 392 | aattr '[' <constant> ']'
393 | 'stat.' statattr
314 | special 394 | special
315 | func '(' args ')' 395 | func '(' args ')'
316 | '{' perl code block '}' 396 | '{' perl code block '}'
317 397
318 func = <any function name> 398 func = <any function name>
319 sattr = <any scalar object attribute> 399 sattr = <any scalar object attribute>
320 aattr = <any array object attribute> 400 aattr = <any array object attribute>
321 flag = <any object flag> 401 flag = <any object flag>
402 statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...>
322 special = <any ()-less "function"> 403 special = <any ()-less "function">
323 404
324 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 405 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
325 args = <depends on function> 406 args = <depends on function>
326 407
327 TODO: repeatedly, env, contains, possbly matches 408 TODO: contains, matches, query_name, selling_price, buying_price?
328 409
329=cut 410=cut
330 411
331=head2 PERL FUNCTIONS 412=head2 PERL FUNCTIONS
332 413
337package cf::match; 418package cf::match;
338 419
339use common::sense; 420use common::sense;
340 421
341use List::Util (); 422use List::Util ();
342
343# parser state
344# $_ # string to be parsed
345our $all; # find all, or just the first matching object
346 423
347{ 424{
348 package cf::match::exec; 425 package cf::match::exec;
349 426
350 use List::Util qw(first); 427 use List::Util qw(first);
351
352 sub env_chain {
353 my @res;
354 push @res, $_
355 while $_ = $_->env;
356 @res
357 }
358 428
359 package cf::match::parser; 429 package cf::match::parser;
360 430
361 use common::sense; 431 use common::sense;
362 432
363 sub ws { 433 sub ws {
364 /\G\s+/gc; 434 /\G\s+/gc;
365 } 435 }
366 436
437 sub condition ();
438 sub match ($$);
439
367 our %func = ( 440 our %func = (
368 has => sub { 441 has => sub {
369 'first { ' . &condition . ' } $_->inv' 442 'first { ' . condition . ' } $_->inv'
370 }, 443 },
371 count => sub { 444 count => sub {
372 local $all = 1;
373 '(scalar ' . &match . ')' 445 '(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 }, 446 },
385 dump => sub { 447 dump => sub {
386 'do { 448 'do {
387 warn "cf::match::match dump:\n" 449 warn "cf::match::match dump:\n"
388 . "self: " . eval { $self->name } . "\n" 450 . "self: " . eval { $self->name } . "\n"
390 1 452 1
391 }'; 453 }';
392 }, 454 },
393 ); 455 );
394 456
457 our %special = (
458 any => sub {
459 1
460 },
461 none => sub {
462 0
463 },
464 );
465
395 sub constant { 466 sub constant {
396 ws; 467 ws;
397 468
398 return $1 if /\G([\-\+0-9\.]+)/gc; 469 return $1 if /\G([\-\+0-9\.]+)/gc;
399 return "cf::$1" if /\G([A-Z0-9_]+)/gc; 470 return "cf::$1" if /\G([A-Z0-9_]+)/gc;
404 die "number, string or uppercase constant name expected\n"; 475 die "number, string or uppercase constant name expected\n";
405 } 476 }
406 477
407 our $flag = $cf::REFLECT{object}{flags}; 478 our $flag = $cf::REFLECT{object}{flags};
408 our $sattr = $cf::REFLECT{object}{scalars}; 479 our $sattr = $cf::REFLECT{object}{scalars};
480 # quick hack to support archname, untested
481 $sattr->{archname} = "W";
409 our $aattr = $cf::REFLECT{object}{arrays}; 482 our $aattr = $cf::REFLECT{object}{arrays};
483 our $lattr = $cf::REFLECT{living}{scalars};
410 484
411 sub expr { 485 sub expr {
412 # ws done by factor 486 # ws done by factor
413 my $res; 487 my $res;
414 488
417 491
418 my $expr = $1; 492 my $expr = $1;
419 493
420 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 494 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
421 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
422 } elsif (/\G([A-Za-z0-9_]+)/gc) { 506 } elsif (/\G([A-Za-z0-9_]+)/gc) {
423 507
424 if (my $func = $func{$1}) { 508 if (my $func = $func{$1}) {
425 /\G\s*\(/gc 509 /\G\s*\(/gc
426 or die "'(' expected after function name\n"; 510 or die "'(' expected after function name\n";
454 } else { 538 } else {
455 $res .= constant; 539 $res .= constant;
456 } 540 }
457 541
458 } else { 542 } else {
543 Carp::cluck;#d#
459 die "expr expected\n"; 544 die "expr expected\n";
460 } 545 }
461 546
462 $res 547 $res
463 } 548 }
480 $res .= "!"; 565 $res .= "!";
481 } 566 }
482 567
483 if (/\G\(/gc) { 568 if (/\G\(/gc) {
484 # () 569 # ()
485 $res .= &condition; 570
486 ws; 571 $res .= '(' . (match 0, '$_') . ')';
572
487 /\G\)/gc or die "')' expected\n"; 573 /\G\s*\)/gc or die "closing ')' expected\n";
488 574
489 } else { 575 } else {
490 my $expr = expr; 576 my $expr = expr;
491 577
492 $res .= $expr; 578 $res .= $expr;
503 } 589 }
504 590
505 "($res)" 591 "($res)"
506 } 592 }
507 593
508 sub condition { 594 sub condition () {
509 my $res = factor; 595 my $res = factor;
510 596
511 while () { 597 while () {
512 ws; 598 ws;
599
600 # first check some stop-symbols, so we don't have to backtrack
513 if (/\G(?=also\b|in\b|\)|$)/gc) { 601 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
514 # early stop => faster and requires no backtracking 602 pos = pos; # argh. the misop hits again. again. again. again. you die.
515 last; 603 last;
604
516 } elsif (/\Gor\b/gc) { 605 } elsif (/\Gor\b/gc) {
517 $res .= " || "; 606 $res .= " || ";
607
518 } else { 608 } else {
519 /\Gand\b/gc; 609 /\Gand\b/gc;
520 $res .= " && "; 610 $res .= " && ";
521 } 611 }
522 $res .= factor; 612 $res .= factor;
523 } 613 }
524 614
525 $res 615 $res
526 } 616 }
527 617
528 sub match { 618 sub match ($$) {
529 my $res; 619 my ($wantarray, $defctx) = @_;
530 620
531 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
532 while () { 633 while () {
533 if (/\G\s*(inv|env|map|object|subject|originator)\b/gc) { 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
642 if (/\G(inv|env|map|arch|head)\b/gc) {
534 if ($1 eq "inv") { 643 if ($1 eq "inv") {
535 $res .= " map+(${also}\$_->inv),"; 644 $expand = "map \$_->inv,";
536 } elsif ($1 eq "env") { 645 } elsif ($1 eq "env") {
537 $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
538 } elsif ($1 eq "map") { 653 } elsif ($1 eq "map") {
539 $res .= " map+(${also}\$_->map->at (\$_->x, \$_->y)),"; 654 $expand = "map \$_->map->at (\$_->x, \$_->y),";
540 } elsif ($1 eq "self") { 655 $deep = 0; # infinite loop otherwise
541 return "$res \$self"; 656 }
542 } elsif ($1 eq "object") { 657 } else {
543 last; # default 658 $expand = "map \$_->inv, grep { " . condition . " }";
544 } elsif ($1 eq "source") {
545 return "$res \$source";
546 } elsif ($1 eq "originator") {
547 return "$res \$originator";
548 } 659 }
549 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 }
550 } else { 673 } else {
551 $res .= " map+($also\$_->inv)," if defined $also;
552 $res .= $all ? " grep { " : " first {";
553 $res .= condition;
554 $res .= "}";
555 674
556 $also = /\G\s*also\b/gc ? '$_, ' : ''; 675 if (/\Gof\s+(self|object|source|originator)\b/gc) {
557 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 }
558 } 691 }
559 } 692 }
560
561 "$res \$object"
562 } 693 }
563
564} 694}
565 695
566sub parse($;$) { 696sub parse($$) { # wantarray, matchexpr
567 local $_ = shift; 697 my $res;
568 local $all = shift;
569 698
699 local $_ = $_[1];
700
701 eval {
570 my $res = cf::match::parser::match; 702 $res = cf::match::parser::match $_[0], "\$object";
703
704 /\G$/gc
705 or die "unexpected trailing characters after match\n";
706 };
571 707
572 if ($@) { 708 if ($@) {
573 my $ctx = 20; 709 my $ctx = 20;
574 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 710 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
575 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 711 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
577 chomp $@; 713 chomp $@;
578 die "$@ ($str)\n"; 714 die "$@ ($str)\n";
579 } 715 }
580 716
581 $res 717 $res
718}
719
720if (0) {#d#
721 die parse 1, 'stats.pow';
722 exit 0;
723}
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
582} 741}
583 742
584=item cf::match::match $match, $object[, $self[, $source[, $originator]]] 743=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
585 744
586Compiles (and caches) the C<$match> expression and matches it against 745Compiles (and caches) the C<$match> expression and matches it against
592In list context it finds and returns all matching objects, in scalar 751In list context it finds and returns all matching objects, in scalar
593context only a true or false value. 752context only a true or false value.
594 753
595=cut 754=cut
596 755
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($$;$$$) { 756sub match($$;$$$) {
617 my $match = shift; 757 my $match = shift;
618 my $all = wantarray+0; 758 my $wantarray = wantarray+0;
619 759
620 &{ 760 &{
621 $CACHE{"$all$match"} ||= compile $match, $all 761 $CACHE{"$wantarray$match"} ||= compile $wantarray, $match
622 } 762 }
623} 763}
624 764
765our $CACHE_CLEARER = AE::timer 3600, 3600, sub {
766 %CACHE = ();
767};
768
625#d# $::schmorp=cf::player::find "schmorp"& 769#d# $::schmorp=cf::player::find "schmorp"&
626#d# cf::match::match '', $::schmorp->ob 770#d# cf::match::match '', $::schmorp->ob
627 771
628 772
629=back 773=back

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines