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.9 by root, Sun Oct 11 00:24:35 2009 UTC vs.
Revision 1.13 by root, Sun Oct 11 23:51:47 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. 71Sometimes the server is only interested in knowing whether I<anything>
72matches, and sometimes the server is interested in I<all> objects that
73match.
60 74
61=head2 OPERATORS 75=head2 OPERATORS
62 76
63=over 4 77=over 4
64 78
68expressions. C<not> negates the expression, and parentheses can be used to 82expressions. C<not> negates the expression, and parentheses can be used to
69group conditions. 83group conditions.
70 84
71Example: match applied weapons. 85Example: match applied weapons.
72 86
73 type=WEAPON and applied 87 applied type=WEAPON
74 88
75Example: match horns or rods. 89Example: match horns or rods.
76 90
77 type=HORN or type=ROD 91 type=HORN or type=ROD
78 92
84the match, the C<in> operator lets you look at other sets of objects, most 98the match, the C<in> operator lets you look at other sets of objects, most
85often the inventory. 99often the inventory.
86 100
87=over 4 101=over 4
88 102
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 103=item in inv
121 104
122Replaces all objects by their inventory. 105Replaces all objects by their inventory.
123 106
124Example: find all spell objects inside the object to be matched. 107Example: find all spell objects inside the object to be matched.
153 136
154Example: check if the context object I<is> a spell, or I<contains> a spell. 137Example: check if the context object I<is> a spell, or I<contains> a spell.
155 138
156 type=SPELL also in inv 139 type=SPELL also in inv
157 140
158=item repeatedly in ... 141=item deep in ...
159 142
160Repeats the operation as many times as possible. This can be used to 143Repeats the operation as many times as possible. This can be used to
161recursively look into objects. 144recursively look into objects.
162 145
163=item also repeatedly in ... 146=item also deep in ...
164 147
165C<also> and C<repeatedly> can be combined. 148C<also> and C<deep> can be combined.
166 149
167Example: check if there are any unpaid items in an inventory, 150Example: check if there are any unpaid items in an inventory,
168or in the inventories of the inventory objects, and so on. 151or in the inventories of the inventory objects, and so on.
169 152
170 unpaid also repeatedly in inv 153 unpaid also deep in inv
171 154
172Example: check if a object is inside a player. 155Example: check if a object is inside a player.
173 156
174 type=PLAYER also repeatedly in env 157 type=PLAYER also deep in env
175 158
176=back 159=back
160
161=item of ...
162
163By default, all matches are applied to the "obviously appropriate" object,
164such as the item dropped on a button or moving over a detector. This can
165be changed to a number of other objects - not all of them are available
166for each match (when not available, the match will simply fail).
167
168An C<of> term ends a match, nothing is allowed to follow.
169
170=over 4
171
172=item of object
173
174Starts with the default object - this is the object passed to the match to
175match against by default. Matches have an explicit C<of object> appended,
176but submatches start at the current object, and in this case C<of object>
177can be used to start at the original object once more.
178
179=item of source
180
181Starts with the I<source> object - this object is sometimes passed to
182matches and represents the object that is the source of the action, such
183as a rod or a potion when it is applied. Often, the I<source> is the same
184as the I<originator>.
185
186=item of originator
187
188Starts with the I<originator> - one step farther removed than the
189I<source>, the I<originator> is sometimes passed to matches and represents
190the original initiator of an action, most commonly a player or monster.
191
192This object is often identical to the I<source> (e.g. when a player casts
193a spell, the player is both source and originator).
194
195=item of self
196
197Starts with the object initiating/asking for the match - this is basically
198always the object that the match expression is attached to.
177 199
178=back 200=back
179 201
180=head2 EXPRESSIONS 202=head2 EXPRESSIONS
181 203
212=item { BLOCK } 234=item { BLOCK }
213 235
214You can specify perl code to execute by putting it inside curly 236You can specify perl code to execute by putting it inside curly
215braces. The last expression evaluated inside will become the result. 237braces. The last expression evaluated inside will become the result.
216 238
239The perlcode can access C<$_>, which rferes to the object currently being
240matches, and the C<$object>, C<$self>, C<$source> and C<$originator>.
241
242Example: check whether the slaying field consists of digits only.
243
244 { $_->slaying =~ /^\d+$/ }
245
217=item comparisons, <, <=, ==, =, !=, =>, > 246=item comparisons, <, <=, ==, =, !=, =>, >
218 247
219You can compare expressions against constants via any of these 248You can compare expressions against constants via any of these
220operators. If the constant is a string, then a string compare will be 249operators. If the constant is a string, then a string compare will be
221done, otherwise a numerical comparison is used. 250done, otherwise a numerical comparison is used.
243=item any 272=item any
244 273
245This simply evaluates to true, and simply makes matching I<any> object a 274This simply evaluates to true, and simply makes matching I<any> object a
246bit easier to read. 275bit easier to read.
247 276
248=item has(cond) 277=item has(condition)
249 278
250True iff the object has a matching inventory object. 279True iff the object has a matching inventory object.
251 280
252=item count(match) 281=item count(match)
253 282
254Number of matching objects - the context object for the C<match> are the 283Number of matching objects - the context object for the C<match> is the
255original context objects for the overall C<match>. # TODO bullshit 284currently tested object - you can override this with an C<in object> for
285example.
256 286
257=item match(match) 287=item match(match)
258 288
259An independent match - unlike C<count>, it only matters whether the match 289An independent match - semantics like C<count>, except it only matters
260finds any object (which is faster). 290whether the match finds any object (which is faster).
291
292=item dump
293
294Dumps the object to the server log when executed, and evaluates to true.
295
296Note that logical operations are short-circuiting, so this only dumps
297potions:
298
299 type=POTION and dump
261 300
262=back 301=back
263 302
264=head2 GRAMMAR 303=head2 GRAMMAR
265 304
267module. It is meant to be easily readable by humans, not to implement it 306module. It is meant to be easily readable by humans, not to implement it
268exactly as-is. 307exactly as-is.
269 308
270 # object matching and selecting 309 # object matching and selecting
271 310
272 match = set 311 match = chain
312 | chain 'of' root
313 root = 'object' | 'self' | 'source' | 'originator'
314 chain = condition
273 | match also rep 'in' set 315 | chain also deep 'in' set
274 also = nothing | 'also' 316 also = nothing | 'also'
275 rep = nothing | 'rep' | 'repeatedly' 317 deep = nothing | 'deep'
276
277 set = 'inv' | 'env' | 'map' 318 set = 'inv' | 'env' | 'map'
278 | 'object' | 'source' | 'originator' | 'self'
279 319
280 empty = 320 empty =
281 321
282 # boolean matching condition 322 # boolean matching condition
283 323
306 special = <any ()-less "function"> 346 special = <any ()-less "function">
307 347
308 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 348 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
309 args = <depends on function> 349 args = <depends on function>
310 350
311 TODO: repeatedly, env, contains, possbly matches 351 TODO: contains, matches, query_name, selling_price, buying_price?
312 352
313=cut 353=cut
314 354
355=head2 PERL FUNCTIONS
356
357=over 4
358
359=cut
315 360
316package cf::match; 361package cf::match;
317 362
318use common::sense; 363use common::sense;
319 364
325 370
326{ 371{
327 package cf::match::exec; 372 package cf::match::exec;
328 373
329 use List::Util qw(first); 374 use List::Util qw(first);
330
331 sub env_chain {
332 my @res;
333 push @res, $_
334 while $_ = $_->env;
335 @res
336 }
337 375
338 package cf::match::parser; 376 package cf::match::parser;
339 377
340 use common::sense; 378 use common::sense;
341 379
347 has => sub { 385 has => sub {
348 'first { ' . &condition . ' } $_->inv' 386 'first { ' . &condition . ' } $_->inv'
349 }, 387 },
350 count => sub { 388 count => sub {
351 local $all = 1; 389 local $all = 1;
352 '(scalar ' . &match . ')' 390 '(scalar ' . &match ('$_') . ')'
353 }, 391 },
354 match => sub { 392 match => sub {
355 local $all = 0; 393 local $all = 0;
356 '(scalar ' . &match . ')' 394 '(scalar ' . &match ('$_') . ')'
357 }, 395 },
358 ); 396 );
359 397
360 our %special = ( 398 our %special = (
361 any => sub { 399 any => sub {
362 1 400 1
401 },
402 dump => sub {
403 'do {
404 warn "cf::match::match dump:\n"
405 . "self: " . eval { $self->name } . "\n"
406 . $_->as_string;
407 1
408 }';
363 }, 409 },
364 ); 410 );
365 411
366 sub constant { 412 sub constant {
367 ws; 413 ws;
479 sub condition { 525 sub condition {
480 my $res = factor; 526 my $res = factor;
481 527
482 while () { 528 while () {
483 ws; 529 ws;
530
531 # first check some stop-symbols, so we don't have to backtrack
484 if (/\G(?=also\b|in\b|\)|$)/gc) { 532 if (/\G(?=also\b|deep\b|in\b|of\b\)|$)/gc) {
485 # early stop => faster and requires no backtracking
486 last; 533 last;
534
487 } elsif (/\Gor\b/gc) { 535 } elsif (/\Gor\b/gc) {
488 $res .= " || "; 536 $res .= " || ";
537
489 } else { 538 } else {
490 /\Gand\b/gc; 539 /\Gand\b/gc;
491 $res .= " && "; 540 $res .= " && ";
492 } 541 }
493 $res .= factor; 542 $res .= factor;
495 544
496 $res 545 $res
497 } 546 }
498 547
499 sub match { 548 sub match {
500 my $res; 549 my $default = shift;
501 550
502 my $also; # undef means first iteration 551 my $res = ($all ? " grep { " : " first {") . condition . " }";
552
503 while () { 553 while () {
504 if (/\G\s*(inv|env|map|object|subject|originator)\b/gc) { 554 ws;
555
556 my $also = /\Galso\s+/gc + 0;
557 my $deep = /\Gdeep\s+/gc + 0;
558
559 if (/\Gin\s+/gc) {
560 my $expand;
561
562 if (/\G(inv|env|map)\b/gc) {
505 if ($1 eq "inv") { 563 if ($1 eq "inv") {
506 $res .= " map+(${also}\$_->inv),"; 564 $expand = "map \$_->inv,";
507 } elsif ($1 eq "env") { 565 } elsif ($1 eq "env") {
508 $res .= " map+(${also}env_chain), "; # TODO 566 $expand = "map \$_->env // (),";
509 } elsif ($1 eq "map") { 567 } elsif ($1 eq "map") {
510 $res .= " map+(${also}\$_->map->at (\$_->x, \$_->y)),"; 568 $expand = "map \$_->map->at (\$_->x, \$_->y),";
569 }
570 } else {
571 $expand = "map \$_->inv, grep { " . condition . " }";
572 }
573
574 if ($also || $deep) {
575 $res .= " do {\n"
576 . " my \@res;\n";
577 $res .= " while (\@_) {\n" if $deep;
578 $res .= " push \@res, \@_;\n" if $also;
579 $res .= " \@_ = $expand \@_;\n";
580 $res .= " }\n" if $deep;
581 $res .= " (\@res, \@_)\n"
582 . "}";
583 } else {
584 $res .= " $expand";
585 }
586 } elsif (/\Gof\s+(self|object|source|originator)\b/gc) {
587 $also || $deep
588 and die "neither 'also' nor 'deep' can be used with 'of'\n";
589
511 } elsif ($1 eq "self") { 590 if ($1 eq "self") {
512 return "$res \$self"; 591 return "$res \$self // ()";
513 } elsif ($1 eq "object") { 592 } elsif ($1 eq "object") {
514 last; # default 593 return "$res \$object";
515 } elsif ($1 eq "source") { 594 } elsif ($1 eq "source") {
516 return "$res \$source"; 595 return "$res \$source // ()";
517 } elsif ($1 eq "originator") { 596 } elsif ($1 eq "originator") {
518 return "$res \$originator"; 597 return "$res \$originator // \$source // ()";
519 } 598 }
520 last unless /\G\s*in\b/gc;
521 } else { 599 } else {
522 $res .= " map+($also\$_->inv)," if defined $also; 600 return "$res $default";
523 $res .= $all ? " grep { " : " first {";
524 $res .= condition;
525 $res .= "}";
526
527 $also = /\G\s*also\b/gc ? '$_, ' : '';
528 last unless /\G\s*in\b/gc;
529 } 601 }
530 } 602 }
531
532 "$res \$object"
533 } 603 }
534
535} 604}
536 605
537sub parse($;$) { 606sub parse($;$) {
538 local $_ = shift; 607 local $_ = shift;
539 local $all = shift; 608 local $all = shift;
540 609
610 my $res;
611
612 eval {
541 my $res = cf::match::parser::match; 613 $res = cf::match::parser::match "\$object";
614
615 /\G\s*$/gc
616 or die "unexpected trailing characters after match\n";
617 };
542 618
543 if ($@) { 619 if ($@) {
544 my $ctx = 20; 620 my $ctx = 20;
545 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 621 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
546 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 622 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
550 } 626 }
551 627
552 $res 628 $res
553} 629}
554 630
631if (0) {#d#
632 die parse 'applied', 1;
633 exit 0;
634}
635
636=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
637
638Compiles (and caches) the C<$match> expression and matches it against
639the C<$object>. C<$self> should be the object initiating the match (or
640C<undef>), C<$source> should be the actor/source and C<$originator> the
641object that initiated the action (such as the player). C<$originator>
642defaults to C<$source> when not given.
643
644In list context it finds and returns all matching objects, in scalar
645context only a true or false value.
646
647=cut
648
555our %CACHE; 649our %CACHE;
556 650
557sub match($$$;$$) { 651sub compile($$) {
558 my ($self, $match, $object, $source, $originator) = @_; 652 my ($match, $all) = @_;
559 my $all = wantarray+0;
560
561 $originator ||= $source;
562
563 &{
564 $CACHE{"$all$match"} ||= do {
565 my $expr = parse $match, $all; 653 my $expr = parse $match, $all;
566 warn "$match,$all => $expr\n";#d# 654 warn "MATCH DEBUG $match,$all => $expr\n";#d#
567 $expr = eval "package cf::match::exec; sub { $expr }"; 655 $expr = eval "
568 die if $@; 656 package cf::match::exec;
657 sub {
658 my (\$object, \$self, \$source, \$originator) = \@_;
569 $expr 659 $expr
570 } 660 }
571 } 661 ";
662 die if $@;
663
664 $expr
572} 665}
573 666
574#match undef, "any", undef; 667sub match($$;$$$) {
575#exit 0; 668 my $match = shift;
669 my $all = wantarray+0;
670
671 &{
672 $CACHE{"$all$match"} ||= compile $match, $all
673 }
674}
675
676#d# $::schmorp=cf::player::find "schmorp"&
677#d# cf::match::match '', $::schmorp->ob
678
679
680=back
681
682=head1 AUTHOR
683
684 Marc Lehmann <schmorp@schmorp.de>
685 http://home.schmorp.de/
686
687=cut
576 688
5771; 6891;
578 690

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines