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.2 by root, Sat Oct 10 05:17:46 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
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
16Match the object if it has an object with name C<force> and 39Match the object if it has an object with name C<force> and
17slaying C<poison> in it's inventory: 40slaying C<poison> in it's inventory:
18 41
19 has (name = "force" and slaying = "poison") 42 has (name = "force" and slaying = "poison")
20 43
21Find all inventory objects with value > 10, which are not invisible: 44Find all inventory objects with value >= 10, which are not invisible:
22 45
23 value > 10 and not invisible in inv 46 value >= 10 and not invisible in inv
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 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
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 objetc is 90Once the final set of context objects has been established, each object
92matched against the C<match> expression. Sometimes the server is only 91is matched against the C<condition>.
93interested in knowing whether I<anything> matches, and sometimes the 92
94server is interested in I<all> objects that match. 93It is possible to chain modifiers from right-to-left, so this example
94would start with the originator, take it's inventory, find all inventory
95items which are potions, looks into their inventory, and then finds all
96spells.
97
98 type=SPELL in type=POTION in inv of originator
99
100Sometimes the server is only interested in knowing whether I<anything>
101matches, and sometimes the server is interested in I<all> objects that
102match.
95 103
96=head2 OPERATORS 104=head2 OPERATORS
97 105
98=over 4 106=over 4
99 107
100=item and, or, not, () 108=item and, or, not, ()
101 109
102Match expressions can be combined with C<and> or C<or> to build larger 110Conditions can be combined with C<and> or C<or> to build larger
103expressions. C<not> negates the expression, and parentheses can be used to 111expressions. C<not> negates the condition, and parentheses can be used to
104group match expressions. 112override operator precedence and execute submatches.
113
114Not that C<not> only negates a condition and not the whole match
115expressions, thus
116
117 not applied in inv
118
119is true if there is I<any> non-applied object in the inventory. To negate
120a whole match, you have to use a sub-match: To check whether there is
121I<no> applied object in someones inventory, write this:
122
123 not (applied in inv)
105 124
106Example: match applied weapons. 125Example: match applied weapons.
107 126
108 type=WEAPON and applied 127 applied type=WEAPON
109 128
110Example: match horns or rods. 129Example: match horns or rods.
111 130
112 type=HORN or type=ROD 131 type=HORN or type=ROD
113 132
133Example: see if the originator is a player.
134
135 type=PLAYER of originator
136
114=item in ... 137=item in ...
115 138
116The in operator takes the context set and modifies it in various ways. 139The in operator takes the context set and modifies it in various ways. As
140a less technical description, think of the C<in> as being a I<look into>
141or I<look at> operator - instead of looking at whatever was provided to
142the match, the C<in> operator lets you look at other sets of objects, most
143often the inventory.
117 144
118=over 4 145=over 4
119 146
120=item in inv 147=item in inv
121 148
127 154
128=item in env 155=item in env
129 156
130Replaces all objects by their containing object, if they have one. 157Replaces all objects by their containing object, if they have one.
131 158
159=item in arch
160
161Replaces all objects by their archetypes.
162
132=item in map 163=item in map
133 164
134Replaces all objects by the objects that are on the same mapspace as them. 165Replaces all objects by the objects that are on the same mapspace as them.
135 166
136=item in <match> 167=item in head
137 168
169Replaces all objects by their head objects.
170
171=item in <condition>
172
138Finds all context objects matching the match expression, and then puts 173Finds all context objects matching the condition, and then puts their
139their inventories into the context set. 174inventories into the context set.
140 175
141Note that C<in inv> is simply a special case of an C<< in <match> >> that 176Note that C<in inv> is simply a special case of an C<< in <condition> >> that
142matches any object. 177matches any object.
143 178
144Example: find all spells inside potions inside the inventory of the context 179Example: find all spells inside potions inside the inventory of the context
145object(s). 180object(s).
146 181
153 188
154Example: check if the context object I<is> a spell, or I<contains> a spell. 189Example: check if the context object I<is> a spell, or I<contains> a spell.
155 190
156 type=SPELL also in inv 191 type=SPELL also in inv
157 192
158=item repeatedly in ... 193=item also deep in ...
159 194
160Repeats the operation as many times as possible. This can be used to 195Repeats the operation as many times as possible. This can be used to
161recursively look into objects. 196recursively look into objects.
162 197
163=item also repeatedly in ... 198So for example, C<also deep in inv> means to take the inventory of all
199objects, taking their inventories, and so on, and adding all these objects
200to the context set.
164 201
165C<also> and C<repeatedly> can be combined. 202Similarly, C<also deep in env> means to take the environment object, their
203environemnt object and so on.
166 204
167Example: check if there are any unpaid items in an inventory, 205Example: check if there are any unpaid items in an inventory,
168or in the inventories of the inventory objects, and so on. 206or in the inventories of the inventory objects, and so on.
169 207
170 unpaid also repeatedly in inv 208 unpaid also deep in inv
171 209
172Example: check if a object is inside a player. 210Example: check if a object is inside a player.
173 211
174 type=PLAYER also repeatedly in env 212 type=PLAYER also deep in env
175 213
176=back 214=back
177 215
216=item of ...
217
218By default, all matches are applied to the "obviously appropriate" object,
219such as the item dropped on a button or moving over a detector. This can
220be changed to a number of other objects - not all of them are available
221for each match (when not available, the match will simply fail).
222
223An C<of> term ends a match, nothing is allowed to follow.
224
225=over 4
226
227=item of object
228
229Starts with the default object - this is the object passed to the match to
230match against by default. Matches have an explicit C<of object> appended,
231but submatches start at the current object, and in this case C<of object>
232can be used to start at the original object once more.
233
234=item of source
235
236Starts with the I<source> object - this object is sometimes passed to
237matches and represents the object that is the source of the action, such
238as a rod or a potion when it is applied. Often, the I<source> is the same
239as the I<originator>.
240
241=item of originator
242
243Starts with the I<originator> - one step farther removed than the
244I<source>, the I<originator> is sometimes passed to matches and represents
245the original initiator of an action, most commonly a player or monster.
246
247This object is often identical to the I<source> (e.g. when a player casts
248a spell, the player is both source and originator).
249
250=item of self
251
252Starts with the object initiating/asking for the match - this is basically
253always the object that the match expression is attached to.
254
178=back 255=back
179 256
180=head2 EXPRESSIONS 257=head2 EXPRESSIONS
181 258
182Match expressions usually consist of simple boolean checks (flag XYZ is 259Expressions used in conditions usually consist of simple boolean checks
183set) or simple comparisons. 260(flag XYZ is set) or simple comparisons.
184 261
185=over 4 262=over 4
186 263
187=item flags 264=item flags
188 265
198=item array objects attributes 275=item array objects attributes
199 276
200The C<resist> array can be accessed by specifying C<< resist [ ATNR_type ] 277The C<resist> array can be accessed by specifying C<< resist [ ATNR_type ]
201>>. 278>>.
202 279
203Example: match an acid resitance higher than 30. 280Example: match an acid resistance higher than 30.
204 281
205 resist[ATNR_ACID] > 30 282 resist[ATNR_ACID] > 30
206 283
207=item functions 284=item functions
208 285
211 288
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.
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+$/ }
216 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.
222 306
223Example: match an object with name "schnops" that has a value > 10. 307Example: match an object with name "schnops" that has a value >= 10.
224 308
225 name="schnops" and value > 10 309 name="schnops" and value >= 10
226 310
227=item uppercase constant names 311=item uppercase constant names
228 312
229Any uppercase word that exists as constant inide the C<cf::> namespace 313Any uppercase word that exists as constant inside the C<cf::> namespace
230(that is, any deliatra constant) can also be used as-is, but needs to be 314(that is, any deliantra constant) can also be used as-is, but needs to be
231specified in uppercase. 315specified in uppercase.
232 316
233Example: match a type of POTION (using C<cf::POTION>). 317Example: match a type of POTION (using C<cf::POTION>).
234 318
235 type=POTION 319 type=POTION
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 inventory objects. 343Number of matching objects - the context object for the C<match> is the
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()
255 355
256=back 356=back
257 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
258=cut 410=cut
259 411
412=head2 PERL FUNCTIONS
413
414=over 4
415
416=cut
260 417
261package cf::match; 418package cf::match;
262 419
263use common::sense; 420use common::sense;
264 421
265use List::Util (); 422use List::Util ();
266
267# parser state
268# $_ # string to be parsed
269our $all; # find all, or just the first matching object
270 423
271{ 424{
272 package cf::match::exec; 425 package cf::match::exec;
273 426
274 our @ctx; # root object(s)
275
276 use List::Util qw(first); 427 use List::Util qw(first);
277
278 sub env_chain {
279 my @res;
280 push @res, $_
281 while $_ = $_->env;
282 @res
283 }
284 428
285 package cf::match::parser; 429 package cf::match::parser;
286 430
287 use common::sense; 431 use common::sense;
288 432
289 sub ws { 433 sub ws {
290 /\G\s+/gc; 434 /\G\s+/gc;
291 } 435 }
292 436
437 sub condition ();
438 sub match ($$);
439
293 our %func = ( 440 our %func = (
294 has => sub { 441 has => sub {
295 'first { ' . &match . ' } $_->inv' 442 'first { ' . condition . ' } $_->inv'
296 }, 443 },
297 count => sub { 444 count => sub {
298 local $all = 1; 445 '(scalar ' . (match 1, '$_') . ')'
299 '(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 }';
300 }, 454 },
301 ); 455 );
302 456
303 our %special = ( 457 our %special = (
304 any => sub { 458 any => sub {
305 1 459 1
306 }, 460 },
461 none => sub {
462 0
463 },
307 ); 464 );
308 465
309 sub constant { 466 sub constant {
310 ws; 467 ws;
311 468
318 die "number, string or uppercase constant name expected\n"; 475 die "number, string or uppercase constant name expected\n";
319 } 476 }
320 477
321 our $flag = $cf::REFLECT{object}{flags}; 478 our $flag = $cf::REFLECT{object}{flags};
322 our $sattr = $cf::REFLECT{object}{scalars}; 479 our $sattr = $cf::REFLECT{object}{scalars};
480 # quick hack to support archname, untested
481 $sattr->{archname} = "W";
323 our $aattr = $cf::REFLECT{object}{arrays}; 482 our $aattr = $cf::REFLECT{object}{arrays};
483 our $lattr = $cf::REFLECT{living}{scalars};
324 484
325 sub expr { 485 sub expr {
326 # ws done by factor 486 # ws done by factor
327 my $res; 487 my $res;
328 488
331 491
332 my $expr = $1; 492 my $expr = $1;
333 493
334 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; 494 $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr";
335 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
336 } elsif (/\G([A-Za-z0-9_]+)/gc) { 506 } elsif (/\G([A-Za-z0-9_]+)/gc) {
337 507
338 if (my $func = $func{$1}) { 508 if (my $func = $func{$1}) {
339 /\G\s*\(/gc 509 /\G\s*\(/gc
340 or die "'(' expected after function name\n"; 510 or die "'(' expected after function name\n";
368 } else { 538 } else {
369 $res .= constant; 539 $res .= constant;
370 } 540 }
371 541
372 } else { 542 } else {
543 Carp::cluck;#d#
373 die "expr expected\n"; 544 die "expr expected\n";
374 } 545 }
375 546
376 $res 547 $res
377 } 548 }
394 $res .= "!"; 565 $res .= "!";
395 } 566 }
396 567
397 if (/\G\(/gc) { 568 if (/\G\(/gc) {
398 # () 569 # ()
399 $res .= &match; 570
400 ws; 571 $res .= '(' . (match 0, '$_') . ')';
572
401 /\G\)/gc or die "')' expected\n"; 573 /\G\s*\)/gc or die "closing ')' expected\n";
402 574
403 } else { 575 } else {
404 my $expr = expr; 576 my $expr = expr;
405 577
406 $res .= $expr; 578 $res .= $expr;
417 } 589 }
418 590
419 "($res)" 591 "($res)"
420 } 592 }
421 593
422 sub match { 594 sub condition () {
423 my $res = factor; 595 my $res = factor;
424 596
425 while () { 597 while () {
426 ws; 598 ws;
599
600 # first check some stop-symbols, so we don't have to backtrack
427 if (/\G(?=also\b|in\b|\)|$)/gc) { 601 if (/\G(?=also\b|deep\b|in\b|of\b|\)|\z)/gc) {
428 # early stop => faster and requires no backtracking 602 pos = pos; # argh. the misop hits again. again. again. again. you die.
429 last; 603 last;
604
430 } elsif (/\Gor\b/gc) { 605 } elsif (/\Gor\b/gc) {
431 $res .= " || "; 606 $res .= " || ";
607
432 } else { 608 } else {
433 /\Gand\b/gc; 609 /\Gand\b/gc;
434 $res .= " && "; 610 $res .= " && ";
435 } 611 }
436 $res .= factor; 612 $res .= factor;
437 } 613 }
438 614
439 $res 615 $res
440 } 616 }
441 617
442 sub select { 618 sub match ($$) {
443 my $res; 619 my ($wantarray, $defctx) = @_;
444 620
445 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
446 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
447 if (/\G\s*(inv|env|map)\b/gc) { 642 if (/\G(inv|env|map|arch|head)\b/gc) {
448 if ($1 eq "inv") { 643 if ($1 eq "inv") {
449 $res .= " map+(${also}\$_->inv),"; 644 $expand = "map \$_->inv,";
450 } elsif ($1 eq "env") { 645 } elsif ($1 eq "env") {
451 $res .= " map+(${also}env_chain), "; 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
452 } elsif ($1 eq "map") { 653 } elsif ($1 eq "map") {
453 $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 . " }";
454 } 659 }
455 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 }
456 } else { 673 } else {
457 $res .= " map+($also\$_->inv)," if defined $also;
458 $res .= $all ? " grep { " : " first {";
459 $res .= match;
460 $res .= "}";
461 674
462 $also = /\G\s*also\b/gc ? '$_, ' : ''; 675 if (/\Gof\s+(self|object|source|originator)\b/gc) {
463 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 }
464 } 691 }
465 } 692 }
466
467 "$res \@ctx"
468 } 693 }
469
470} 694}
471 695
472sub parse($;$) { 696sub parse($$) { # wantarray, matchexpr
473 local $_ = shift; 697 my $res;
474 local $all = shift;
475 698
476 my $res = "package cf::match::exec;\n" 699 local $_ = $_[1];
477 . eval { cf::match::parser::select }; 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 };
478 707
479 if ($@) { 708 if ($@) {
480 my $ctx = 20; 709 my $ctx = 20;
481 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 710 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
482 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 711 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
486 } 715 }
487 716
488 $res 717 $res
489} 718}
490 719
491if (0) { 720if (0) {#d#
492 my $perl = parse 'flag(SEE_IN_DARK) in inv', 0; 721 die parse 1, 'stats.pow';
493
494 warn $perl, "\n";#d#
495 $perl = eval "no warnings; no feature; sub { $perl }"; die if $@;
496 use B::Deparse;
497 warn B::Deparse->new->coderef2text ($perl);
498 exit 0; 722 exit 0;
499} 723}
500 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
5011; 7821;
502 783

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines