File Coverage

lib/Chemistry/OpenSMILES/Parser.yp
Criterion Covered Total %
statement 242 247 97.9
branch 112 120 93.3
condition 53 60 88.3
subroutine 27 27 100.0
pod 1 2 50.0
total 435 456 95.3


line stmt bran cond sub pod time code
1             # Header section
2              
3             %{
4              
5             =head1 NAME
6              
7             Chemistry::OpenSMILES::Parser - OpenSMILES format reader
8              
9             =head1 SYNOPSIS
10              
11             use Chemistry::OpenSMILES::Parser;
12              
13             my $parser = Chemistry::OpenSMILES::Parser->new;
14             my @moieties = $parser->parse( 'C#C.c1ccccc1' );
15              
16             $\ = "\n";
17             for my $moiety (@moieties) {
18             # $moiety is a Graph::Undirected object
19             print scalar $moiety->vertices;
20             print scalar $moiety->edges;
21             }
22              
23             =head1 DESCRIPTION
24              
25             C is OpenSMILES format reader.
26              
27             =cut
28              
29 45     45   331 use warnings;
  45         73  
  45         2339  
30 45     45   602 use 5.0100;
  45         111  
31              
32 45     45   19484 use Chemistry::Elements;
  45         307463  
  45         3215  
33 45         6166 use Chemistry::OpenSMILES qw(
34             %bond_symbol_to_order
35             %normal_valence
36             is_aromatic
37             is_chiral
38             toggle_cistrans
39 45     45   12095 );
  45         107  
40 45     45   216 use Graph::Undirected;
  45         53  
  45         1105  
41 45     45   147 use List::Util qw( any first sum0 );
  45         85  
  45         160397  
42              
43             =head1 METHODS
44              
45             =head2 C
46              
47             Parses a SMILES string and returns an array of disconnected molecular entities as separate instances of L.
48             Their interpretation is described in detail in L.
49              
50             =head3 Options
51              
52             C accepts the following options for key-value pairs in an anonymous hash for its second parameter:
53              
54             =over
55              
56             =item C
57              
58             In OpenSMILES specification the number of attached hydrogen atoms for atoms in square brackets is limited to 9.
59             IUPAC SMILES+ has increased this number to 99.
60             With the value of C the parser can be instructed to allow other than 1 digit for attached hydrogen count.
61              
62             =item C
63              
64             With C set to anything evaluating to true, the parser will not convert neither implicit nor explicit hydrogen atoms in square brackets to atom hashes of their own.
65             Moreover, it will not attempt to unify the representations of chirality.
66             It should be noted, though, that many of subroutines of Chemistry::OpenSMILES expect non-raw data structures, thus processing raw output may produce distorted results.
67             In particular, C calls from L have to be instructed to expect raw data structure:
68              
69             write_SMILES( \@moieties, { raw => 1 } );
70              
71             This option is now deprecated and may be removed in upcoming versions.
72              
73             =item C
74              
75             With C set to anything evaluating to true, the parser will emit warnings about bot bonds used in parentheses.
76             This could be used to detect the unnecessary use of dot bonds.
77              
78             =back
79              
80             =head1 CAVEATS
81              
82             Deprecated charge notations (C<--> and C<++>) are supported.
83              
84             OpenSMILES specification mandates a strict order of ring bonds and branches:
85              
86             branched_atom ::= atom ringbond* branch*
87              
88             Chemistry::OpenSMILES::Parser supports both the mandated, and inverted
89             structure, where ring bonds follow branch descriptions.
90              
91             Whitespace is not supported yet. SMILES descriptors must be cleaned of
92             it before attempting reading with Chemistry::OpenSMILES::Parser.
93              
94             The derivation of implicit hydrogen counts for aromatic atoms is not
95             unambiguously defined in the OpenSMILES specification. Thus only
96             aromatic carbon is accounted for as if having valence of 3.
97              
98             Chiral atoms with three neighbours are interpreted as having a lone pair of electrons one of its chiral neighbours.
99             The lone pair is always understood as being the second in the order of neighbour enumeration, except when the atom with the lone pair starts a chain.
100             In that case lone pair is the first.
101              
102             =cut
103              
104             %}
105              
106             %%
107 181     181 0 7869255  
108 181 50       546 # Rules section
109              
110             # The top-level 'filter' rule
111              
112             smiles: chain ;
113              
114             chain: atom
115             {
116 762     762   24420 my $g = Graph::Undirected->new( refvertexed => 1 );
117 762         188392 $g->add_vertex( $_[1] );
118 762         38547 push @{$_[0]->{USER}{GRAPHS}}, $g;
  762         1654  
119              
120 762         1342 $_[1]->{graph} = $g;
121 762         712 $_[1]->{index} = @{$_[0]->{USER}{GRAPHS}}-1;
  762         1684  
122              
123 762         2330 return { first => $_[1],
124             last => $_[1] };
125             }
126             | chain atom
127             {
128 719     719   20335 $_[2]->{graph} = $_[1]->{last}{graph};
129 719         1178 $_[2]->{index} = $_[1]->{last}{index};
130              
131 719         1928 $_[2]->{graph}->add_edge( $_[1]->{last}, $_[2] );
132              
133 719 100 100     110465 if( is_aromatic $_[1]->{last} && is_aromatic $_[2] ) {
134             $_[2]->{graph}->set_edge_attribute( $_[1]->{last},
135 180         3417 $_[2],
136             'bond',
137             ':' );
138             }
139              
140 719         35055 _push_chirality_neighbour( $_[1]->{last}, $_[2] );
141 719         1301 _push_chirality_neighbour( $_[2], $_[1]->{last} );
142              
143 719         931 $_[1]->{last} = $_[2];
144              
145 719         1058 return $_[1];
146             }
147             | chain bond atom
148             {
149 204     204   5893 $_[3]->{graph} = $_[1]->{last}{graph};
150 204         345 $_[3]->{index} = $_[1]->{last}{index};
151              
152 204 100       341 if( $_[2] ne '-' ) {
153             $_[3]->{graph}->set_edge_attribute( $_[1]->{last},
154 196         3657 $_[3],
155             'bond',
156             $_[2] );
157             } else {
158 8         22 $_[3]->{graph}->add_edge( $_[1]->{last}, $_[3] );
159             }
160              
161 204         70769 _push_chirality_neighbour( $_[1]->{last}, $_[3] );
162 204         379 _push_chirality_neighbour( $_[3], $_[1]->{last} );
163              
164 204         253 $_[1]->{last} = $_[3];
165              
166 204         351 return $_[1];
167             }
168             | chain '.' atom
169             {
170 33     33   968 my $g = Graph::Undirected->new( refvertexed => 1 );
171 33         3876 $g->add_vertex( $_[3] );
172 33         1537 push @{$_[0]->{USER}{GRAPHS}}, $g;
  33         80  
173              
174 33         54 $_[3]->{graph} = $g;
175 33         36 $_[3]->{index} = @{$_[0]->{USER}{GRAPHS}}-1;
  33         67  
176              
177 33         47 $_[1]->{last} = $_[3];
178              
179 33         125 return $_[1];
180             }
181             | chain '(' chain ')'
182             {
183 379 100 66 379   12029 if( $_[0]->{USER}{OPTIONS}{report_unnecessary_dot_usage} &&
184             $_[3]->{first}{index} != $_[3]->{last}{index} ) {
185 2         16 warn 'unnecessary use of dot in parenthesis' . "\n";
186             }
187              
188 379 100       880 if( $_[1]->{last}{index} != $_[3]->{first}{index} ) {
189             $_[0]->_merge_graphs( $_[1]->{last}{index},
190 353         930 $_[3]->{first}{index} );
191             }
192              
193 379         1082 $_[1]->{last}{graph}->add_edge( $_[1]->{last}, $_[3]->{first} );
194              
195 379 100 100     46233 if( is_aromatic $_[1]->{last} && is_aromatic $_[3]->{first} ) {
196             $_[1]->{last}{graph}->set_edge_attribute( $_[1]->{last},
197             $_[3]->{first},
198 25         445 'bond',
199             ':' );
200             }
201              
202 379         5464 _push_chirality_neighbour( $_[1]->{last}, $_[3]->{first} );
203 379         749 _unshift_chirality_neighbour( $_[3]->{first}, $_[1]->{last} );
204              
205 379         674 return $_[1];
206             }
207             | chain '(' bond chain ')'
208             {
209 46 100 66 46   1494 if( $_[0]->{USER}{OPTIONS}{report_unnecessary_dot_usage} &&
210             $_[4]->{first}{index} != $_[4]->{last}{index} ) {
211 1         9 warn 'unnecessary use of dot in parenthesis' . "\n";
212             }
213              
214 46 100       135 if( $_[1]->{last}{index} != $_[4]->{first}{index} ) {
215             $_[0]->_merge_graphs( $_[1]->{last}{index},
216 43         133 $_[4]->{first}{index} );
217             }
218              
219 46 100       129 if( $_[3] ne '-' ) {
220             $_[1]->{last}{graph}->set_edge_attribute( $_[1]->{last},
221             $_[4]->{first},
222 44         966 'bond',
223             $_[3] );
224             } else {
225             $_[1]->{last}{graph}->add_edge( $_[1]->{last},
226 2         7 $_[4]->{first} );
227             }
228              
229 46         14315 _push_chirality_neighbour( $_[1]->{last}, $_[4]->{first} );
230 46         112 _unshift_chirality_neighbour( $_[4]->{first}, $_[1]->{last} );
231              
232 46         102 return $_[1];
233             }
234             | chain '(' '.' chain ')'
235             {
236 7 100   7   214 if( $_[0]->{USER}{OPTIONS}{report_unnecessary_dot_usage} ) {
237 4         35 warn 'unnecessary use of dot in parenthesis' . "\n";
238             }
239              
240 7         19 return $_[1];
241             }
242              
243             # According to the specification of OpenSMILES, ring bonds are
244             # allowed only before the branch enumeration. However, I think this
245             # is too strict.
246              
247             | chain ringbond
248             {
249 233     233   7017 $_[0]->_add_ring_bond( $_[1]->{last}, $_[2] );
250 232         373 return $_[1];
251             }
252             | chain bond ringbond
253             {
254 32     32   887 $_[0]->_add_ring_bond( $_[1]->{last}, $_[3], $_[2] );
255 31         47 return $_[1];
256             }
257 181         11647 ;
258              
259             bond: '-' | '=' | '#' | '$' | ':' | '/' | '\\' ;
260              
261             %%
262              
263             # Footer section
264              
265             sub _Error
266             {
267 10     10   170 my( $self ) = @_;
268 10 50       17 close $self->{USER}{FILEIN} if $self->{USER}{FILEIN};
269              
270 10 100 100     6 if( ${$self->{TOKEN}} eq '' &&
  10         25  
271 7 100 100     20 grep { defined $_ && !ref $_ && $_ eq '(' }
272 7         8 map { $_->[1] } @{$self->{STACK}} ) {
  2         5  
273 1         47 die "$0: syntax error: missing closing parenthesis.\n";
274             }
275              
276 9 100       8 if( ${$self->{TOKEN}} eq ')' ) {
  9         18  
277 2         132 die "$0: syntax error: unbalanced parentheses.\n";
278             }
279              
280 7         24 my $msg = "$0: syntax error at position $self->{USER}{CHARNO}";
281 181 100       10833 if( $self->YYData->{INPUT} ) {
  7         31  
282 6         26 $self->YYData->{INPUT} =~ s/\n$//;
283 6         28 die "$msg: '" . $self->YYData->{INPUT} . "'.\n";
284             } else {
285 1         66 die "$msg.\n";
286             }
287             }
288              
289             sub _Lexer
290             {
291 3511     3511   79711 my( $self ) = @_;
292              
293             # If the line is empty and the input is originating from the file,
294             # another line is read.
295 3511 50 66     5294 if( !$self->YYData->{INPUT} && $self->{USER}{FILEIN} ) {
296 0         0 my $filein = $self->{USER}{FILEIN};
297 0         0 $self->YYData->{INPUT} = <$filein>;
298 0         0 $self->{USER}{CHARNO} = 0;
299             }
300              
301 3511 50       18067 if( $self->YYData->{INPUT} =~ s/^(\s+)// ) {
302 0         0 $self->{USER}{CHARNO} += length $1;
303             }
304              
305 3511         18117 my $hcount_re = 'H[0-9]?';
306 3511 100       5653 if( defined $self->{USER}{OPTIONS}{max_hydrogen_count_digits} ) {
307             $hcount_re = sprintf 'H[0-9]{0,%d}',
308 2         14 $self->{USER}{OPTIONS}{max_hydrogen_count_digits};
309             }
310              
311             # Bracket atoms
312 3511 100       4112 if( $self->YYData->{INPUT} =~ s/^\[ (?[0-9]+)?
313             (?[A-Za-z][a-z]?|\*)
314             (?@(
315             (TH|AL)[12] |
316             SP [123] |
317             (TB|OH)[0-9]{1,2} |
318             @?
319             ))?
320             (? $hcount_re)?
321             (?--|\+\+|[-+][0-9]{0,2})?
322             (:(?[0-9]+))? \]//x ) {
323 239         6375 my $atom = { %+, number => $self->{USER}{ATOMNO} };
324 239         570 $self->{USER}{ATOMNO} ++;
325 239         467 $self->{USER}{CHARNO} += length $&;
326              
327             # Check for existence of the seen element
328             # Due to https://github.com/briandfoy/chemistry-elements/issues/16, Chemistry::Elements < 1.079 has 'Ha' instead of 'Db'
329 239 100 100     2471 if( $atom->{symbol} eq '*' || $atom->{symbol} eq 'Db' ) {
    100 100        
    100 100        
330             # OK
331             } elsif( $atom->{symbol} eq lc $atom->{symbol} &&
332             $atom->{symbol} !~ /^(as|se|[bcnops])$/ ) {
333 2         15 die "aromatic chemical element '$atom->{symbol}' is not allowed\n";
334             } elsif( $atom->{symbol} eq 'Ha' ||
335             !Chemistry::Elements->new( $atom->{symbol} ) ) {
336 5         165 die "chemical element with symbol '$atom->{symbol}' is unknown\n";
337             }
338              
339 232 100       13530 if( $atom->{charge} ) {
340 23         403 $atom->{charge} =~ s/^([-+])$/${1}1/;
341 23         66 $atom->{charge} =~ s/^([-+])\1$/${1}2/;
342 23         53 $atom->{charge} = int $atom->{charge};
343             }
344              
345 232 100       2984 if( $atom->{hcount} ) {
346 29         73 $atom->{hcount} =~ s/^H//;
347 29 100       73 $atom->{hcount} = $atom->{hcount} ? int $atom->{hcount} : 1;
348             } else {
349 203         298 $atom->{hcount} = 0;
350             }
351              
352 232 100       762 if( $atom->{isotope} ) {
353 6         9 $atom->{isotope} = int $atom->{isotope};
354             }
355              
356             # Atom class is an arbitrary number, 0 by default
357 232 100       421 $atom->{class} = exists $atom->{class} ? int $atom->{class} : 0;
358              
359 232         701 return ( 'atom', $atom );
360             }
361              
362             # Bracketless atoms
363 3272 100       29407 if( $self->YYData->{INPUT} =~ s/^(Br|Cl|[BCINOPSFbcnops*])// ) {
364             my $atom = { symbol => $1,
365             class => 0,
366 1486         12673 number => $self->{USER}{ATOMNO} };
367 1486         1681 $self->{USER}{ATOMNO} ++;
368 1486         2229 $self->{USER}{CHARNO} += length $&;
369 1486         3887 return ( 'atom', $atom );
370             }
371              
372             # Ring bonds
373 1786 100 100     8663 if( $self->YYData->{INPUT} =~ s/^%([0-9]{2})// ||
374             $self->YYData->{INPUT} =~ s/^([0-9])// ) {
375 265         2887 $self->{USER}{CHARNO} += length $&;
376 265         859 return ( 'ringbond', int $1 );
377             }
378              
379 1521         13440 my $char = substr( $self->YYData->{INPUT}, 0, 1 );
380 1521 100       7093 if( $char ne '' ) {
381 1199         1424 $self->YYData->{INPUT} = substr( $self->YYData->{INPUT}, 1 );
382             }
383 1521         8195 $self->{USER}{CHARNO} ++;
384 1521         3431 return( $char, $char );
385             }
386              
387             sub parse
388             {
389 339     339 1 106564 my( $self, $string, $options ) = @_;
390 339 100       841 $options = {} unless $options;
391              
392 339         1013 $self->YYData->{INPUT} = $string;
393 339         4138 $self->{USER}{GRAPHS} = [];
394 339         669 $self->{USER}{RINGBONDS} = {};
395 339         568 $self->{USER}{ATOMNO} = 0;
396 339         463 $self->{USER}{CHARNO} = 0;
397 339         571 $self->{USER}{OPTIONS} = $options;
398             $self->YYParse( yylex => \&_Lexer,
399             yyerror => \&_Error,
400 339         1643 yydebug => $options->{debug} );
401              
402 320 100       20555 if( scalar keys %{$self->{USER}{RINGBONDS}} ) {
  320         887  
403             die "$0: unclosed ring bond(s) detected: " .
404 2         7 join( ', ', sort { $a <=> $b } keys %{$self->{USER}{RINGBONDS}} ) .
  1         47  
  2         74  
405             ".\n";
406             }
407              
408 318         431 my @graphs = grep { defined } @{$self->{USER}{GRAPHS}};
  783         1157  
  318         561  
409 318         504 for my $graph (@graphs) {
410 351         776 for my $atom (sort { $a->{number} <=> $b->{number} } $graph->vertices) {
  2334         7743  
411 1697         3081 delete $atom->{graph};
412 1697         1625 delete $atom->{index};
413 1697 100       2409 next if $options->{raw};
414              
415             # Promote implicit hydrogen atoms into explicit ones
416 955 100       1425 if( !exists $atom->{hcount} ) {
417 800 100       1514 next if !exists $normal_valence{$atom->{symbol}};
418 1336 100 100     181956 my $degree = sum0 map { $_ ne ':' && exists $bond_symbol_to_order{$_} ? $bond_symbol_to_order{$_} : 1 }
419 798 100       1668 map { $graph->has_edge_attribute( $atom, $_, 'bond' )
  1336         222873  
420             ? $graph->get_edge_attribute( $atom, $_, 'bond' )
421             : '-' }
422             $graph->neighbours( $atom );
423 802     802   1087 my $valence = first { $degree <= $_ }
424 798         3893 @{$normal_valence{$atom->{symbol}}};
  798         2132  
425 798 100       2245 next unless defined $valence;
426 794         1367 $atom->{hcount} = $valence - $degree;
427             }
428 949         1989 for (1..$atom->{hcount}) {
429             my $hydrogen = { symbol => 'H',
430             class => 0,
431 925         2428 number => $self->{USER}{ATOMNO} };
432 925         1962 $graph->add_edge( $atom, $hydrogen );
433 925         130691 $self->{USER}{ATOMNO} ++;
434 925 100       1746 if( is_chiral $atom ) {
435 8 50       20 if( $atom->{chirality_neighbours} ) {
436 8 100   16   25 if( any { $_->{number} < $atom->{number} } @{$atom->{chirality_neighbours}} ) {
  16         28  
  8         25  
437 4         5 splice @{$atom->{chirality_neighbours}}, 1, 0, $hydrogen;
  4         19  
438             } else {
439 4         9 _unshift_chirality_neighbour( $atom, $hydrogen );
440             }
441             } else {
442             # This only happens if chiral atom does not have neighbours other than implicit hydrogens.
443             # This is degenerate case anyway.
444 0         0 _push_chirality_neighbour( $atom, $hydrogen );
445             }
446             }
447             }
448 949         1136 delete $atom->{hcount};
449              
450             # Unify the representation of chirality
451 949 100       1459 if( is_chiral $atom ) {
452 57 100       305 if( $atom->{chirality} =~ /^@@?$/ ) {
453 54 100       227 if( $graph->degree( $atom ) == 2 ) {
    100          
    100          
454 9         2896 $atom->{chirality} =~ s/@+/'@AL' . length $&/e;
  9         36  
455             } elsif( $graph->degree( $atom ) == 5 ) {
456 1         970 $atom->{chirality} =~ s/@+/'@TB' . length $&/e;
  1         4  
457             } elsif( $graph->degree( $atom ) == 6 ) {
458 2         3356 $atom->{chirality} =~ s/@+/'@OH' . length $&/e;
  2         8  
459             }
460             }
461              
462 57         55321 $atom->{chirality} =~ s/^\@TH1$/@/;
463 57         132 $atom->{chirality} =~ s/^\@TH2$/@@/;
464             }
465             }
466             }
467              
468 318         1558 return @graphs;
469             }
470              
471             sub _add_ring_bond
472             {
473 265     265   512 my( $self, $atom, $ring_bond, $bond ) = @_;
474 265 100       590 if( $self->{USER}{RINGBONDS}{$ring_bond} ) {
475             $self->_merge_graphs( $self->{USER}{RINGBONDS}{$ring_bond}{atom}{index},
476 131         427 $atom->{index} );
477              
478 131 50 100     429 if( $bond && $self->{USER}{RINGBONDS}{$ring_bond}{bond} &&
      33        
      66        
479             (($bond !~ /^[\\\/]$/ &&
480             $bond ne $self->{USER}{RINGBONDS}{$ring_bond}{bond}) ||
481             ($bond eq '\\' &&
482             $self->{USER}{RINGBONDS}{$ring_bond}{bond} ne '/') ||
483             ($bond eq '/' &&
484             $self->{USER}{RINGBONDS}{$ring_bond}{bond} ne '\\')) ) {
485 1         49 die "$0: ring bond types for ring bond $ring_bond do not match.\n";
486             }
487 244     244   390 $bond = first { defined }
488 130         847 ( $self->{USER}{RINGBONDS}{$ring_bond}{bond}, $bond );
489              
490 130 100 100     532 if( $bond && !defined $self->{USER}{RINGBONDS}{$ring_bond}{bond} ) {
491             # If cis/trans marker is not specified when cis/trans bond is
492             # seen first, it has to be inverted:
493 2         9 $bond = toggle_cistrans $bond;
494             }
495              
496 130         206 my $ring_atom = $self->{USER}{RINGBONDS}{$ring_bond}{atom};
497 130 100       294 die "atom cannot be bonded to itself\n" if $atom == $ring_atom;
498 129 100 100     405 if( !$bond && is_aromatic $ring_atom && is_aromatic $atom ) {
      100        
499 40         78 $bond = ':';
500             }
501 129 100 100     338 if( $bond && $bond ne '-' ) {
502 55         995 $atom->{graph}->set_edge_attribute( $ring_atom,
503             $atom,
504             'bond',
505             $bond );
506             } else {
507 74         182 $atom->{graph}->add_edge( $ring_atom, $atom );
508             }
509 129         26516 delete $self->{USER}{RINGBONDS}{$ring_bond};
510              
511 129 50 66     293 if( is_chiral $ring_atom && $ring_atom->{chirality_neighbours} ) {
512             my $pos = first { !ref $ring_atom->{chirality_neighbours}[$_] &&
513 7 100   7   25 $ring_atom->{chirality_neighbours}[$_] == $ring_bond }
514 4         16 0..$#{$ring_atom->{chirality_neighbours}};
  4         13  
515 4 50       19 $ring_atom->{chirality_neighbours}[$pos] = $atom if defined $pos;
516             }
517 129         270 _push_chirality_neighbour( $atom, $ring_atom );
518             } else {
519 134 100       439 $self->{USER}{RINGBONDS}{$ring_bond} =
520             { atom => $atom, $bond ? ( bond => $bond ) : () };
521              
522             # Record a placeholder for later addition of real chirality
523             # neighbour, which will be identified by the ring bond number
524 134         305 _push_chirality_neighbour( $atom, $ring_bond );
525             }
526             }
527              
528             sub _merge_graphs
529             {
530 527     527   739 my( $self, $index1, $index2 ) = @_;
531 527 100       860 return if $index1 == $index2;
532              
533 432         589 my $g1 = $self->{USER}{GRAPHS}[$index1];
534 432         548 my $g2 = $self->{USER}{GRAPHS}[$index2];
535              
536 432         854 for ($g2->vertices) {
537 948         7573 $_->{graph} = $g1;
538 948         1062 $_->{index} = $index1;
539             }
540 432         645 $g1->add_vertices( $g2->vertices );
541              
542 432         33073 for ($g2->edges) {
543 529         76473 my $attributes = $g2->get_edge_attributes( @$_ );
544 529 100       84774 if( $attributes ) {
545 151         1683 $g1->set_edge_attributes( @$_, $attributes );
546             } else {
547 378         654 $g1->add_edge( @$_ );
548             }
549             }
550              
551 432         27406 $self->{USER}{GRAPHS}[$index2] = undef;
552             }
553              
554             sub _push_chirality_neighbour
555             {
556 2534     2534   2938 my( $atom1, $atom2 ) = @_;
557 2534 100       3627 return unless is_chiral $atom1;
558 344         386 push @{$atom1->{chirality_neighbours}}, $atom2;
  344         645  
559             }
560              
561             sub _unshift_chirality_neighbour
562             {
563 429     429   496 my( $atom1, $atom2 ) = @_;
564 429 100       558 return unless is_chiral $atom1;
565 7         11 unshift @{$atom1->{chirality_neighbours}}, $atom2;
  7         30  
566             }
567              
568             1;
569              
570             =head1 AUTHORS
571              
572             Andrius Merkys, Emerkys@cpan.orgE
573              
574             =cut