File Coverage

blib/lib/Chemistry/File/SMARTS.pm
Criterion Covered Total %
statement 597 597 100.0
branch 106 128 82.8
condition 3 6 50.0
subroutine 199 199 100.0
pod 1 7 14.2
total 906 937 96.6


line stmt bran cond sub pod time code
1             package Chemistry::File::SMARTS;
2              
3             $VERSION = "0.22";
4             # $Id: SMARTS.pm,v 1.8 2005/05/16 22:20:24 itubert Exp $
5              
6 3     3   163996 use 5.006;
  3         10  
  3         146  
7 2     2   12 use strict;
  2         4  
  2         70  
8 2     2   12 use warnings;
  2         8  
  2         62  
9 2     2   919 use Chemistry::Pattern;
  2         58210  
  2         111  
10 2     2   16 use base "Chemistry::File";
  2         3  
  2         1035  
11 2     2   16354 use Carp;
  2         4  
  2         134  
12             #use Data::Dumper;
13 2     2   11 use List::Util 'sum', 'first';
  2         5  
  2         1584  
14              
15             =head1 NAME
16              
17             Chemistry::File::SMARTS - SMARTS chemical substructure pattern linear notation parser
18              
19             =head1 SYNOPSYS
20              
21             #!/usr/bin/perl
22             use Chemistry::File::SMARTS;
23              
24             # this string matches an oxygen next to an atom with three
25             # neighbors, one of which is a hydrogen, and a positive charge
26             my $smarts = 'O[D3H+]';
27              
28             # parse a SMARTS string and compile it into a
29             # Chemistry::Pattern object
30             my $patt = Chemistry::Pattern->parse("$smarts", format => 'smarts');
31              
32             # find matches of the pattern in a Chemistry::Mol object $mol
33             my $mol = Chemistry::Mol->read("myfile.mol");
34             while ($patt->match($mol)) {
35             print "pattern matches atoms: ", $patt->atom_map, "\n"
36             }
37              
38             # NOTE: if the SMARTS pattern relies on aromaticity or ring
39             # properties, you have to make sure that the target
40             # molecule is "aromatized" first:
41             my $smarts = 'c:a';
42             my $patt = Chemistry::Pattern->parse("$smarts", format => 'smarts');
43             use Chemistry::Ring 'aromatize_mol';
44             aromatize_mol($mol); # <--- AROMATIZE!!!
45             while ($patt->match($mol)) {
46             print "pattern matches atoms: ", $patt->atom_map, "\n"
47             }
48              
49             # Note that "atom mapping numbers" end up as $atom->name
50             my $patt = Chemistry::Pattern->parse("[C:7][C:8]", format => 'smarts');
51             print $patt->atoms(1)->name; # prints 7
52              
53              
54             =head1 DESCRIPTION
55              
56             This module parse a SMARTS (SMiles ARbitrary Target Specification) string,
57             generating a L object. It is a file I/O driver for the
58             PerlMol toolkit; it's not called directly but by means of the
59             Chemistry::Pattern->parse class method.
60              
61             For a detailed description of the SMARTS language, see
62             L. Note that
63             this module doesn't implement the full language, as detailed under CAVEATS.
64              
65             This module is part of the PerlMol project, L.
66              
67             =cut
68              
69             # Initialization
70             Chemistry::Mol->register_format(smarts => __PACKAGE__);
71             our $DEBUG = 0;
72              
73             # Chemistry::File interface
74              
75             sub parse_string {
76 63     63 1 101496 my ($self, $s, %opts) = @_;
77 63         267 my $patt = parse_smarts($s, \%opts);
78 63         278 $patt->name($s);
79 63         631 $patt;
80             }
81              
82              
83             ############## SMARTS PARSER ##############
84              
85             sub parse_smarts {
86 63     63 0 1495 my ($s, $opts) = @_;
87 63         173 my %digits;
88              
89 63         259 my $toks = tokenize($s);
90 63         173 my $tok = shift(@$toks);
91 63 50       269 print "tok: $tok\n" if $DEBUG;
92 63   50     267 my $mol_class = $opts->{mol_class} || "Chemistry::Pattern";
93 63         281 my $patt = $mol_class->new();
94 63   100     8133 $patt->options($opts->{pattern_options}||{});
95 63         747 my @atom_stack;
96 63         252 my $current_atom = parse_atom($patt, $tok, $toks);
97 63         338 while (defined ($tok = shift @$toks)) {
98 86 50       208 print "tok: $tok\n" if $DEBUG;
99 86 100       284 if ($tok eq '(') {
    100          
100 6         112 push @atom_stack, $current_atom;
101             } elsif ($tok eq ')') {
102 6         31 $current_atom = pop @atom_stack;
103             } else { # bond, atom
104 76         139 my $next_tok = shift @$toks;
105 76 100       337 if ($next_tok =~ /^\d+$/) { # digit
106 9 100       44 if ($digits{$next_tok}) { # close ring
107 5         34 parse_bond($patt, $tok, $current_atom,
108             $digits{$next_tok});
109 5         86 $digits{$next_tok} = undef;
110             } else { # open ring
111 5         34 $digits{$next_tok} = $current_atom;
112             }
113             } else {
114 68         1107 my $next_atom = parse_atom($patt, $next_tok, $toks);
115 68         329 parse_bond($patt, $tok, $current_atom, $next_atom);
116 68         615 $current_atom = $next_atom;
117             }
118             }
119             }
120 63         237 $patt;
121             }
122              
123             sub parse_atom {
124 130     130 0 325 my ($patt, $s, $toks) = @_;
125            
126 130         183 my $n_rec = 0;
127 130         162 my $name;
128              
129 130 100       462 if ($s =~ s/:(\d+)$//) {
130 5         25 $name = $1;
131             }
132 136         402 my $expr =
133             join " and ", map {
134 130         427 join " || ", map {
135 133         600 join ' && ', map {
136 136         307 parse_atomic_primitive($_, \$n_rec);
137             } split '&', $_;
138             } split ',', $_;
139             } split ';', $s;
140              
141            
142 130         253 my @recs;
143 130         328 for (1 .. $n_rec) {
144 8         20 my $rec_smarts = shift @$toks;
145 8         103 my $rec = Chemistry::Pattern->parse($rec_smarts,
146             pattern_options => {overlap=>0, permute=>0},
147             format => 'smarts',
148             );
149 8         45 push @recs, $rec;
150             }
151              
152 130 50       277 print "atom expr: $expr\n" if $DEBUG;
153 2     2   17 my $sub = eval <
  2     2   4  
  2     2   184  
  2     2   9  
  2     1   4  
  2     1   137  
  2     1   15  
  2     1   5  
  2     1   145  
  2     1   12  
  2     1   3  
  2     1   118  
  130     1   11641  
  1     1   162  
  1     1   8  
  1     1   2  
  1     1   91  
  1     1   8  
  1     1   4  
  1     1   95  
  1     1   11  
  1     1   2  
  1     1   99  
  1     1   7  
  1     1   3  
  1     1   72  
  1     1   7  
  1     1   3  
  1     1   72  
  1     1   6  
  1     1   3  
  1     1   60  
  1     1   6  
  1     1   3  
  1     1   58  
  1     1   11  
  1     1   3  
  1     1   74  
  1     1   42  
  1     1   4  
  1     1   112  
  1     1   10  
  1     1   2  
  1     1   85  
  1     1   9  
  1     1   2  
  1     1   116  
  1     1   10  
  1     1   2  
  1     1   1116  
  1     1   9  
  1     1   2  
  1     1   118  
  1     1   6  
  1     1   2  
  1     1   63  
  1     1   9  
  1     1   2  
  1     1   89  
  1     1   6  
  1     1   2  
  1     1   61  
  1     1   6  
  1     1   2  
  1     1   110  
  1     1   6  
  1     1   3  
  1     1   71  
  1     1   8  
  1     1   2  
  1     1   72  
  1     1   8  
  1     1   2  
  1     1   76  
  1     1   7  
  1     1   3  
  1     1   82  
  1     1   6  
  1     1   2  
  1     1   101  
  1     1   8  
  1     1   2  
  1     1   69  
  1     1   6  
  1     1   3  
  1     1   21383  
  1     1   13  
  1     1   3  
  1     1   106  
  1     1   9  
  1     1   2  
  1     1   78  
  1     1   7  
  1     1   2  
  1     1   84  
  1     1   6  
  1     1   2  
  1     1   61  
  1     1   6  
  1     1   3  
  1     1   68  
  1     1   6  
  1     1   3  
  1     1   142  
  1     1   11  
  1     1   3  
  1     1   62  
  1     1   6  
  1     1   2  
  1     1   46  
  1     1   5  
  1     1   2  
  1     1   76  
  1     1   5  
  1     1   3  
  1     1   48  
  1     1   7  
  1     1   2  
  1     1   217  
  1         7  
  1         2  
  1         68  
  1         11  
  1         1  
  1         74  
  1         6  
  1         2  
  1         61  
  1         5  
  1         4  
  1         53  
  1         5  
  1         3  
  1         57  
  1         7  
  1         2  
  1         74  
  1         11  
  1         4  
  1         86  
  1         8  
  1         3  
  1         76  
  1         8  
  1         3  
  1         227  
  1         8  
  1         3  
  1         84  
  1         6  
  1         3  
  1         61  
  1         12  
  1         2  
  1         110  
  1         7  
  1         3  
  1         69  
  1         11  
  1         3  
  1         78  
  1         6  
  1         2  
  1         52  
  1         6  
  1         4  
  1         54  
  1         5  
  1         3  
  1         70  
  1         7  
  1         2  
  1         66  
  1         7  
  1         3  
  1         55  
  1         7  
  1         3  
  1         1091  
  1         6  
  1         3  
  1         51  
  1         6  
  1         2  
  1         75  
  1         6  
  1         2  
  1         58  
  1         11  
  1         3  
  1         51  
  1         8  
  1         3  
  1         58  
  1         5  
  1         3  
  1         50  
  1         180  
  1         3  
  1         56  
  1         6  
  1         3  
  1         47  
  1         7  
  1         2  
  1         58  
  1         7  
  1         2  
  1         80  
  1         8  
  1         3  
  1         61  
  1         11  
  1         11  
  1         73  
  1         6  
  1         2  
  1         77  
  1         7  
  1         3  
  1         72  
  1         11  
  1         2  
  1         108  
  1         10  
  1         2  
  1         80  
  1         11  
  1         3  
  1         92  
  1         6  
  1         2  
  1         156  
  1         10  
  1         3  
  1         98  
  1         11  
  1         1  
  1         95  
  1         7  
  1         3  
  1         61  
  1         6  
  1         2  
  1         78  
  1         7  
  1         3  
  1         62  
  1         5  
  1         2  
  1         57  
  1         6  
  1         3  
  1         62  
  1         6  
  1         3  
  1         58  
  1         10  
  1         3  
  1         62  
  1         6  
  1         3  
  1         77  
  1         7  
  1         3  
  1         71  
  1         6  
  1         3  
  1         114  
  1         9  
  1         3  
  1         64  
  1         6  
  1         3  
  1         59  
154             sub {
155             no warnings;
156             my (\$patt, \$atom) = \@_;
157             $expr;
158             };
159             SUB
160 130         1151 my $atom = $patt->atom_class->new(
161             test_sub => $sub,
162             name => $name,
163             );
164 130         8124 $patt->add_atom($atom);
165 130         3866 $atom;
166             }
167              
168              
169             # missing primitives: @, @@
170             sub parse_atomic_primitive {
171 136     136 0 230 my ($s, $n_rec) = @_;
172 136         384 local $_ = $s;
173 136         241 my @terms;
174 2     2   11 no warnings 'uninitialized';
  2         4  
  2         4796  
175              
176 136 100       414 s/^(!?)H// && # Hydrogen atom
177             push @terms, "$1(\$atom->symbol eq 'H')";
178              
179 136 100       1146 s/(!?)
180             (Zr|Zn|Yb|Y|Xe|W|V|U|Tm|Tl|Ti|Th|
181             Te|Tc|Tb|Ta|Sr|Sn|Sm|Si|Sg|Se|Sc|Sb|S|Ru|Rn|Rh|Rf|Re|Rb|Ra|
182             Pu|Pt|Pr|Po|Pm|Pd|Pb|Pa|P|Os|O|Np|No|Ni|Ne|Nd|Nb|Na|N|Mt|Mt|
183             Mo|Mn|Mg|Md|Lu|Lr|Li|La|Kr|K|Ir|In|I|Hs|Hs|Ho|Hg|Hf|He|Ge|
184             Gd|Ga|Fr|Fm|Fe|F|Eu|Es|Er|Dy|Ds|Db|Cu|Cs|Cr|Co|Cm|Cl|Cf|Ce|
185             Cd|Ca|C|Br|Bk|Bi|Bh|Be|Ba|B|Au|At|As|Ar|Am|Al|Ag|Ac)
186             //x # Order is reverse alphabetical to ensure longest match
187             && push @terms, "$1(\$atom->symbol eq '$2' && ! \$atom->aromatic)";
188              
189 136 100       518 s/(!?)\*// && # wildcard
190             push @terms, "${1}1";
191              
192 136 50       356 s/(!?)D(\d?)// && # explicit connections (shouldn't count h)
    100          
193             push @terms, "$1(\$atom->bonds == " . (length $2 ? $2 : 1) . ')';
194              
195 136 100       376 s/(!?)a// && # aromatic
196             push @terms, "$1\$atom->aromatic";
197              
198 136 100       371 s/(!?)A// && # aliphatic
199             push @terms, "$1(!\$atom->aromatic)";
200              
201 136 50       381 s/(!?)X(\d?)// && # total connections (should add implicit H)
    100          
202             push @terms, "$1(\$atom->bonds + \$atom->hydrogens == "
203             . (length $2 ? $2 : 1) . ')';
204              
205 136 50       315 s/(!?)v(\d?)// && # valence
    100          
206             push @terms, "$1(\$atom->valence == "
207             . (length $2 ? $2 : 1) . ')';
208              
209 136 50       375 s/(!?)H(\d?)// && # total H-count
    100          
210             push @terms, "$1(sum(map {\$_->symbol eq 'H'} \$atom->neighbors) +
211             \$atom->hydrogens == " . (length $2 ? $2 : 1) . ')';
212              
213 136 50       390 s/(!?)h(\d?)// && # implicit H-count
    100          
214             push @terms, "$1(\$atom->hydrogens == " . (length $2 ? $2 : 1) . ')';
215              
216 136 100       324 s/(!?)R(\d?)// && # number of rings
    100          
217             push @terms, "$1(\@{\$atom->attr('ring/rings')||[]} "
218             . (length $2 ? "== $2" : "") . ')';
219            
220 136 100       648 s/(!?)r(\d?)// && # ring size
    100          
221             push @terms, "$1(first { " . (length $2 ? "\$_->atoms == $2" : "1")
222             ." } \@{\$atom->attr('ring/rings')||[]} )";
223            
224 136 100       292 s/(!?)#(\d+)// && # atomic number
225             push @terms, "$1(\$atom->Z == $2)";
226              
227 136 50       276 s/(!?)([+-]\d+)// && # numerical charge
228             push @terms, "$1(\$atom->formal_charge == $2)";
229              
230 136 100       540 s/(!?)(\++)// && # positive charge
231             push @terms, "$1(\$atom->formal_charge == " . length($2) . ')';
232              
233 136 50       263 s/(!?)(-+)// && # negative charge
234             push @terms, "$1(\$atom->formal_charge == -" . length($2) . ')';
235              
236 136 100       346 s/(!?)(\d+)// && # mass
237             push @terms, "$1(\$atom->mass == $2)";
238              
239 136 100       383 s/(!?)([cnosp])// && # aromatic symbol
240 11         79 push @terms, "$1(\$atom->symbol eq '@{[uc $2]}' && \$atom->aromatic)";
241              
242             #s/(!?)([A-Z][a-z]?)// && # aliphatic symbol
243             #push @terms, "$1(\$atom->symbol eq '$2' && ! \$atom->aromatic)";
244              
245 136         359 while (s/(!?)\$//) { # recursive SMARTS
246 8         97 push @terms, qq{$1(\$recs[$$n_rec]->match(\$atom->parent,atom=>\$atom))};
247 8         34 $$n_rec++;
248             }
249              
250 136         1015 join ' && ', @terms;
251             }
252              
253             sub parse_bond {
254 72     72 0 425 my ($patt, $s, @atoms) = @_;
255              
256 72 100       205 return if $s eq '.'; # the disconnected non-bond
257              
258 71         79 my $expr;
259              
260 71 100       452 if ($s) {
261 13         61 $expr =
262             join " and ", map {
263 12         48 join " || ", map {
264 12         37 join ' && ', map {
265 13         119 parse_bond_primitive($_);
266             } split '&', $_;
267             } split ',', $_;
268             } split ';', $s;
269             } else {
270 60         184 $expr = '($bond->order == 1 || $bond->aromatic)';
271             }
272              
273 71 50       248 print "bond expr: $expr\n" if $DEBUG;
274 2     2   11 my $sub = eval <
  2     2   4  
  2     2   127  
  2     1   13  
  2     1   3  
  2     1   114  
  2     1   12  
  2     1   4  
  2     1   119  
  71     1   5487  
  1     1   79  
  1     1   10  
  1     1   3  
  1     1   73  
  1     1   6  
  1     1   2  
  1     1   71  
  1     1   7  
  1     1   1  
  1     1   181  
  1     1   7  
  1     1   3  
  1     1   82  
  1     1   9  
  1     1   3  
  1     1   120  
  1     1   10  
  1     1   2  
  1     1   55  
  1     1   7  
  1     1   2  
  1     1   63  
  1     1   12  
  1     1   2  
  1     1   78  
  1     1   12  
  1     1   3  
  1     1   68  
  1     1   7  
  1     1   2  
  1     1   74  
  1     1   8  
  1     1   3  
  1     1   66  
  1     1   11  
  1     1   2  
  1     1   77  
  1     1   6  
  1     1   4  
  1     1   72  
  1     1   7  
  1     1   2  
  1     1   79  
  1     1   6  
  1     1   3  
  1     1   52  
  1     1   6  
  1     1   3  
  1     1   76  
  1     1   7  
  1     1   2  
  1     1   83  
  1     1   6  
  1     1   2  
  1     1   64  
  1         7  
  1         4  
  1         225  
  1         8  
  1         3  
  1         61  
  1         6  
  1         4  
  1         53  
  1         8  
  1         3  
  1         44  
  1         6  
  1         1  
  1         61  
  1         6  
  1         2  
  1         63  
  1         6  
  1         2  
  1         84  
  1         7  
  1         2  
  1         48  
  1         8  
  1         2  
  1         81  
  1         7  
  1         2  
  1         175  
  1         6  
  1         2  
  1         55  
  1         6  
  1         2  
  1         59  
  1         6  
  1         3  
  1         58  
  1         6  
  1         3  
  1         100  
  1         8  
  1         2  
  1         72  
  1         7  
  1         2  
  1         69  
  1         7  
  1         2  
  1         157  
  1         11  
  1         2  
  1         61  
  1         6  
  1         3  
  1         110  
  1         11  
  1         3  
  1         113  
  1         10  
  1         2  
  1         63  
  1         6  
  1         3  
  1         61  
  1         5  
  1         3  
  1         74  
  1         7  
  1         3  
  1         83  
  1         9  
  1         2  
  1         70  
  1         8  
  1         3  
  1         67  
  1         10  
  1         3  
  1         73  
  1         7  
  1         3  
275             sub {
276             no warnings;
277             my (\$patt, \$bond) = \@_;
278             $expr;
279             };
280             SUB
281 71         325 my $bond = $patt->bond_class->new(test_sub => $sub,
282             atoms => \@atoms);
283 71         8131 $patt->add_bond($bond);
284 71         4017 $bond;
285             }
286              
287             sub parse_bond_primitive {
288 13     13 0 39 local ($_) = @_;
289 13         79 my @terms;
290              
291 13 50       57 s/(!?)~// && # wildcard
292             push @terms, "${1}1";
293              
294 13 100       63 s/(!?)-// && # single
295             push @terms, "$1(\$bond->order == 1 && !\$bond->aromatic)";
296              
297 13 100       170 s/(!?)=// && # double
298             push @terms, "$1(\$bond->order == 2)";
299              
300 13 100       65 s/(!?)#// && # triple
301             push @terms, "$1(\$bond->order == 3)";
302              
303 13 100       50 s/(!?):// && # triple
304             push @terms, "$1\$bond->aromatic";
305              
306 13 100       109 s/(!?)\@// && # ring bond
307             push @terms, "$1(\@{\$bond->attr('ring/rings')||[]} )";
308            
309 13         95 join ' && ', @terms;
310             }
311              
312             my %ORGANIC_ELEMS = (
313             Br => 1, Cl => 1, B => 1, C => 1, N => 1, O => 1, P => 1, S => 1,
314             F => 1, I => 1, s => 1, p => 1, o => 1, n => 1, c => 1, b => 1,
315             );
316              
317             sub tokenize {
318 63     63 0 167 my ($s) = @_;
319 63         240 my $state = 3;
320 63         161 my $paren_depth = 0;
321 63         107 my $atom;
322             my $digit;
323 1         93 my $rec_smart;
324 1         8 my @rec_smarts;
325 63         125 my $bond = '';
326 63         208 my $symbol;
327             my @toks;
328            
329             # 0 expects atom or branch
330             # after atom: atom or bond or branch
331             # after bond: atom or branch or /,;/
332             # token types: atom, bond, (, )
333 63         346 my @chars = split '', $s;
334 63         127 my $char;
335 63         322 while (defined ($char = shift @chars)) {
336 457 50       980 print "char: $char\n" if $DEBUG;
337 457 50       1824 if ($state == 0) { # expect atom or branch (not used!)
    100          
    100          
    100          
    100          
    100          
    50          
    0          
338 1 0       76 push(@toks, $char), $state = 2, next if ($char =~ /[()]/);
339 1         5 $state = 3, redo;
340             } elsif ($state == 1) { # in complex atom
341 108 100       294 if ($char eq ']') {
342 30         147 push @toks, $atom, @rec_smarts;
343 30         76 @rec_smarts = ();
344 30         104 $state = 4, next;
345             }
346 79         199 $atom .= $char;
347 79 100       191 $state = 5 if ($char eq '$'); # recursive smarts
348 79         353 next;
349             } elsif ($state == 2) { # expect bond
350 92 100       299 if ($char =~ /[-=:~\\\/#@?,;&.!]/) {
351 17         45 $bond .= $char;
352 17         49 next;
353             } else {
354 76         216 push @toks, $bond;
355 76         103 $bond = '';
356 76         134 $state = 3, redo;
357             }
358             } elsif ($state == 3) { # expect atom
359 138 100       458 $state = 1, $atom = '', next if $char eq '[';
360 109 50       260 if ($char eq '%') {
361 1         2 $state = 7, next;
362             }
363 109         268 $symbol = $char;
364 109 100       287 push(@toks, $symbol), last unless @chars;
365 71         115 $char = shift @chars;
366 71 100       1067 if ($ORGANIC_ELEMS{$symbol.$char}) {
367 2         9 push @toks, $symbol.$char;
368 2         8 $state = 4, next;
369             } else {
370 70         202 push @toks, $symbol;
371 70         126 $state = 4, redo;
372             }
373             } elsif ($state == 4) { # expect atom or bond or branch
374 86 100       268 push(@toks, $char), next if ($char =~ /[()]/); # branch
375 76         153 $state = 2, redo; # bond
376             } elsif ($state == 5) { # expect left paren
377 8 50       32 croak "expected (" unless $char eq '(';
378 8         14 $rec_smart = '';
379 8         69 $paren_depth++, $state = 6, next;
380             } elsif ($state == 6) { # within recursive smarts
381             #croak "Recursive SMARTS not implemented yet\n";
382 30 50       76 $paren_depth++ if $char eq '(';
383 30 100       60 $paren_depth-- if $char eq ')';
384 30 100       162 unless ($paren_depth) {
385 8         26 push @rec_smarts, $rec_smart;
386 8         23 $state = 1, next;
387             }
388 23         249 $rec_smart .= $char;
389             } elsif ($state == 7) { # double digit
390 1   0     10 $digit = $char . (shift @chars || die "expected second digit");
391 1         2 push @toks, $digit;
392 1         97 $state = 2;
393             } else {
394 1         10 die "shouldn't be here";
395             }
396             }
397             #print Dumper \@toks if $DEBUG;
398 63         268 \@toks;
399             }
400              
401              
402             1;
403              
404             =head1 CAVEATS
405              
406             The following features are not implemented yet:
407              
408             =over
409              
410             =item chirality: @, @@
411              
412             =item component-level gruouping
413              
414             That is, the difference between these three cases:
415              
416             (SMARTS)
417             (SMARTS).(SMARTS)
418             (SMARTS).SMARTS
419              
420             =back
421              
422             The so-called parser is very lenient, so if you give it something that's not
423             quite reasonable it will ignore it or interpret it in a strange way without
424             warning.
425              
426             As shown in the synopsis, you have to make sure that the molecule is
427             "aromatized" if you want to apply to it a pattern that relies on aromaticity
428             or ring properties.
429              
430             =head1 VERSION
431              
432             0.22
433              
434             =head1 SEE ALSO
435              
436             L, L, L,
437             L.
438              
439             For more information about SMARTS, see the SMARTS Theory Manual at
440             L
441              
442             =head1 AUTHOR
443              
444             Ivan Tubert-Brohman Eitub@cpan.orgE
445              
446             =head1 COPYRIGHT
447              
448             Copyright (c) 2005 Ivan Tubert-Brohman. All rights reserved. This program is
449             free software; you can redistribute it and/or modify it under the same terms as
450             Perl itself.
451              
452             =cut
453