ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf/match.pm
(Generate patch)

Comparing deliantra/server/lib/cf/match.pm (file contents):
Revision 1.11 by root, Sun Oct 11 18:18:03 2009 UTC vs.
Revision 1.15 by root, Mon Oct 12 17:37:43 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.
127 110
128=item in env 111=item in env
129 112
130Replaces all objects by their containing object, if they have one. 113Replaces all objects by their containing object, if they have one.
131 114
115=item in arch
116
117Replaces all objects by their archetypes.
118
132=item in map 119=item in map
133 120
134Replaces all objects by the objects that are on the same mapspace as them. 121Replaces all objects by the objects that are on the same mapspace as them.
135 122
136=item in <cond> 123=item in <condition>
137 124
138Finds all context objects matching the condition, and then puts their 125Finds all context objects matching the condition, and then puts their
139inventories into the context set. 126inventories into the context set.
140 127
141Note that C<in inv> is simply a special case of an C<< in <cond> >> that 128Note that C<in inv> is simply a special case of an C<< in <condition> >> that
142matches any object. 129matches any object.
143 130
144Example: find all spells inside potions inside the inventory of the context 131Example: find all spells inside potions inside the inventory of the context
145object(s). 132object(s).
146 133
153 140
154Example: check if the context object I<is> a spell, or I<contains> a spell. 141Example: check if the context object I<is> a spell, or I<contains> a spell.
155 142
156 type=SPELL also in inv 143 type=SPELL also in inv
157 144
158=item repeatedly in ... 145=item deep in ...
159 146
160Repeats the operation as many times as possible. This can be used to 147Repeats the operation as many times as possible. This can be used to
161recursively look into objects. 148recursively look into objects.
162 149
163=item also repeatedly in ... 150=item also deep in ...
164 151
165C<also> and C<repeatedly> can be combined. 152C<also> and C<deep> can be combined.
166 153
167Example: check if there are any unpaid items in an inventory, 154Example: check if there are any unpaid items in an inventory,
168or in the inventories of the inventory objects, and so on. 155or in the inventories of the inventory objects, and so on.
169 156
170 unpaid also repeatedly in inv 157 unpaid also deep in inv
171 158
172Example: check if a object is inside a player. 159Example: check if a object is inside a player.
173 160
174 type=PLAYER also repeatedly in env 161 type=PLAYER also deep in env
175 162
176=back 163=back
164
165=item of ...
166
167By default, all matches are applied to the "obviously appropriate" object,
168such as the item dropped on a button or moving over a detector. This can
169be changed to a number of other objects - not all of them are available
170for each match (when not available, the match will simply fail).
171
172An C<of> term ends a match, nothing is allowed to follow.
173
174=over 4
175
176=item of object
177
178Starts with the default object - this is the object passed to the match to
179match against by default. Matches have an explicit C<of object> appended,
180but submatches start at the current object, and in this case C<of object>
181can be used to start at the original object once more.
182
183=item of source
184
185Starts with the I<source> object - this object is sometimes passed to
186matches and represents the object that is the source of the action, such
187as a rod or a potion when it is applied. Often, the I<source> is the same
188as the I<originator>.
189
190=item of originator
191
192Starts with the I<originator> - one step farther removed than the
193I<source>, the I<originator> is sometimes passed to matches and represents
194the original initiator of an action, most commonly a player or monster.
195
196This object is often identical to the I<source> (e.g. when a player casts
197a spell, the player is both source and originator).
198
199=item of self
200
201Starts with the object initiating/asking for the match - this is basically
202always the object that the match expression is attached to.
177 203
178=back 204=back
179 205
180=head2 EXPRESSIONS 206=head2 EXPRESSIONS
181 207
265=item match(match) 291=item match(match)
266 292
267An independent match - semantics like C<count>, except it only matters 293An independent match - semantics like C<count>, except it only matters
268whether the match finds any object (which is faster). 294whether the match finds any object (which is faster).
269 295
270=item dump 296=item dump()
271 297
272Dumps the object to the server log when executed, and evaluates to true. 298Dumps the object to the server log when executed, and evaluates to true.
273 299
274Note that logical operations are short-circuiting, so this only dumps 300Note that logical operations are short-circuiting, so this only dumps
275potions: 301potions:
276 302
277 type=POTION and dump 303 type=POTION and dump()
278 304
279=back 305=back
280 306
281=head2 GRAMMAR 307=head2 GRAMMAR
282 308
284module. It is meant to be easily readable by humans, not to implement it 310module. It is meant to be easily readable by humans, not to implement it
285exactly as-is. 311exactly as-is.
286 312
287 # object matching and selecting 313 # object matching and selecting
288 314
289 match = set 315 match = chain
316 | chain 'of' root
317 root = 'object' | 'self' | 'source' | 'originator'
318 chain = condition
290 | match also rep 'in' set 319 | chain also deep 'in' set
291 also = nothing | 'also' 320 also = nothing | 'also'
292 rep = nothing | 'rep' | 'repeatedly' 321 deep = nothing | 'deep'
293
294 set = 'inv' | 'env' | 'map' 322 set = 'inv' | 'env' | 'arch' | 'map'
295 | 'object' | 'source' | 'originator' | 'self'
296 323
297 empty = 324 empty =
298 325
299 # boolean matching condition 326 # boolean matching condition
300 327
301 condition = factor 328 condition = factor
302 | factor 'and'? cond 329 | factor 'and'? condition
303 | factor 'or' cond 330 | factor 'or' condition
304 331
305 factor = 'not' factor 332 factor = 'not' factor
306 | '(' cond ')' 333 | '(' condition ')'
307 | expr 334 | expr
308 | expr operator constant 335 | expr operator constant
309 336
310 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>=' 337 operator = '=' | '==' | '!=' | '<' | '<=' | '>' | '>='
311 338
323 special = <any ()-less "function"> 350 special = <any ()-less "function">
324 351
325 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> 352 constant = <number> | '"' <string> '"' | <uppercase cf::XXX name>
326 args = <depends on function> 353 args = <depends on function>
327 354
328 TODO: repeatedly, env, contains, possbly matches 355 TODO: contains, matches, query_name, selling_price, buying_price?
329 356
330=cut 357=cut
331 358
332=head2 PERL FUNCTIONS 359=head2 PERL FUNCTIONS
333 360
347 374
348{ 375{
349 package cf::match::exec; 376 package cf::match::exec;
350 377
351 use List::Util qw(first); 378 use List::Util qw(first);
352
353 sub env_chain {
354 my @res;
355 push @res, $_
356 while $_ = $_->env;
357 @res
358 }
359 379
360 package cf::match::parser; 380 package cf::match::parser;
361 381
362 use common::sense; 382 use common::sense;
363 383
375 }, 395 },
376 match => sub { 396 match => sub {
377 local $all = 0; 397 local $all = 0;
378 '(scalar ' . &match ('$_') . ')' 398 '(scalar ' . &match ('$_') . ')'
379 }, 399 },
380 );
381
382 our %special = (
383 any => sub {
384 1
385 },
386 dump => sub { 400 dump => sub {
387 'do { 401 'do {
388 warn "cf::match::match dump:\n" 402 warn "cf::match::match dump:\n"
389 . "self: " . eval { $self->name } . "\n" 403 . "self: " . eval { $self->name } . "\n"
390 . $_->as_string; 404 . $_->as_string;
391 1 405 1
392 }'; 406 }';
393 }, 407 },
394 ); 408 );
395 409
410 our %special = (
411 any => sub {
412 1
413 },
414 );
415
396 sub constant { 416 sub constant {
397 ws; 417 ws;
398 418
399 return $1 if /\G([\-\+0-9\.]+)/gc; 419 return $1 if /\G([\-\+0-9\.]+)/gc;
400 return "cf::$1" if /\G([A-Z0-9_]+)/gc; 420 return "cf::$1" if /\G([A-Z0-9_]+)/gc;
509 sub condition { 529 sub condition {
510 my $res = factor; 530 my $res = factor;
511 531
512 while () { 532 while () {
513 ws; 533 ws;
534
535 # first check some stop-symbols, so we don't have to backtrack
514 if (/\G(?=also\b|in\b|\)|$)/gc) { 536 if (/\G(?=also\b|deep\b|in\b|of\b\)|\z)/gc) {
515 # early stop => faster and requires no backtracking 537 pos = pos; # argh. the misop hits again. again. again. again. you die.
516 last; 538 last;
539
517 } elsif (/\Gor\b/gc) { 540 } elsif (/\Gor\b/gc) {
518 $res .= " || "; 541 $res .= " || ";
542
519 } else { 543 } else {
520 /\Gand\b/gc; 544 /\Gand\b/gc;
521 $res .= " && "; 545 $res .= " && ";
522 } 546 }
523 $res .= factor; 547 $res .= factor;
527 } 551 }
528 552
529 sub match { 553 sub match {
530 my $default = shift; 554 my $default = shift;
531 555
532 my $res; 556 my $res = ($all ? " grep { " : " first {") . condition . " }";
533 557
534 my $also; # undef means first iteration
535 while () { 558 while () {
536 if (/\G\s*(inv|env|map|object|subject|originator)\b/gc) { 559 ws;
560
561 my $also = /\Galso\s+/gc + 0;
562 my $deep = /\Gdeep\s+/gc + 0;
563
564 if (/\Gin\s+/gc) {
565 my $expand;
566
567 if (/\G(inv|env|map|arch)\b/gc) {
537 if ($1 eq "inv") { 568 if ($1 eq "inv") {
538 $res .= " map+(${also}\$_->inv),"; 569 $expand = "map \$_->inv,";
539 } elsif ($1 eq "env") { 570 } elsif ($1 eq "env") {
540 $res .= " map+(${also}env_chain), "; # TODO 571 $expand = "map \$_->env // (),";
572 } elsif ($1 eq "arch") {
573 $expand = "map \$_->arch,";
541 } elsif ($1 eq "map") { 574 } elsif ($1 eq "map") {
542 $res .= " map+(${also}\$_->map->at (\$_->x, \$_->y)),"; 575 $expand = "map \$_->map->at (\$_->x, \$_->y),";
576 }
577 } else {
578 $expand = "map \$_->inv, grep { " . condition . " }";
579 }
580
581 if ($also || $deep) {
582 $res .= " do {\n"
583 . " my \@res;\n";
584 $res .= " while (\@_) {\n" if $deep;
585 $res .= " push \@res, \@_;\n" if $also;
586 $res .= " \@_ = $expand \@_;\n";
587 $res .= " }\n" if $deep;
588 $res .= " (\@res, \@_)\n"
589 . "}";
590 } else {
591 $res .= " $expand";
592 }
593 } elsif (/\Gof\s+(self|object|source|originator)\b/gc) {
594 $also || $deep
595 and die "neither 'also' nor 'deep' can be used with 'of'\n";
596
543 } elsif ($1 eq "self") { 597 if ($1 eq "self") {
544 return "$res \$self"; 598 return "$res \$self // ()";
545 } elsif ($1 eq "object") { 599 } elsif ($1 eq "object") {
546 return "$res \$object"; 600 return "$res \$object";
547 } elsif ($1 eq "source") { 601 } elsif ($1 eq "source") {
548 return "$res \$source"; 602 return "$res \$source // ()";
549 } elsif ($1 eq "originator") { 603 } elsif ($1 eq "originator") {
550 return "$res \$originator"; 604 return "$res \$originator // \$source // ()";
551 } 605 }
552 last unless /\G\s*in\b/gc;
553 } else { 606 } else {
554 $res .= " map+($also\$_->inv)," if defined $also; 607 return "$res $default";
555 $res .= $all ? " grep { " : " first {";
556 $res .= condition;
557 $res .= "}";
558
559 $also = /\G\s*also\b/gc ? '$_, ' : '';
560 last unless /\G\s*in\b/gc;
561 } 608 }
562 } 609 }
563
564 "$res $default"
565 } 610 }
566
567} 611}
568 612
569sub parse($;$) { 613sub parse($;$) {
570 local $_ = shift; 614 local $_ = shift;
571 local $all = shift; 615 local $all = shift;
572 616
617 my $res;
618
619 eval {
573 my $res = cf::match::parser::match "\$object"; 620 $res = cf::match::parser::match "\$object";
621
622 /\G$/gc
623 or die "unexpected trailing characters after match\n";
624 };
574 625
575 if ($@) { 626 if ($@) {
576 my $ctx = 20; 627 my $ctx = 20;
577 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2; 628 my $str = substr $_, (List::Util::max 0, (pos) - $ctx), $ctx * 2;
578 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->"; 629 substr $str, (List::Util::min $ctx, pos), 0, "<-- HERE -->";
582 } 633 }
583 634
584 $res 635 $res
585} 636}
586 637
587if (0) { 638if (0) {#d#
588 die parse 'match(any in object)', 1; 639 die parse 'type=SPELL_EFFECT and match(name="bullet" in arch)', 1;
589 exit 0; 640 exit 0;
590} 641}
591 642
592=item cf::match::match $match, $object[, $self[, $source[, $originator]]] 643=item cf::match::match $match, $object[, $self[, $source[, $originator]]]
593 644
605our %CACHE; 656our %CACHE;
606 657
607sub compile($$) { 658sub compile($$) {
608 my ($match, $all) = @_; 659 my ($match, $all) = @_;
609 my $expr = parse $match, $all; 660 my $expr = parse $match, $all;
610 warn "$match,$all => $expr\n";#d# 661 warn "MATCH DEBUG $match,$all => $expr\n";#d#
611 $expr = eval " 662 $expr = eval "
612 package cf::match::exec; 663 package cf::match::exec;
613 sub { 664 sub {
614 my (\$object, \$self, \$source, \$originator) = \@_; 665 my (\$object, \$self, \$source, \$originator) = \@_;
615 \$originator ||= \$source;
616 $expr 666 $expr
617 } 667 }
618 "; 668 ";
619 die if $@; 669 die if $@;
620 670

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines