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 44     44   410 use warnings;
  44         109  
  44         3309  
30 44     44   770 use 5.0100;
  44         150  
31              
32 44     44   28717 use Chemistry::Elements;
  44         404187  
  44         4432  
33 44         10267 use Chemistry::OpenSMILES qw(
34             %bond_symbol_to_order
35             %normal_valence
36             is_aromatic
37             is_chiral
38             toggle_cistrans
39 44     44   19731 );
  44         175  
40 44     44   25341 use Graph::Undirected;
  44         2636365  
  44         3245  
41 44     44   498 use List::Util qw( any first sum0 );
  44         89  
  44         254937  
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 180     180 0 12054651  
108 180 50       846 # Rules section
109              
110             # The top-level 'filter' rule
111              
112             smiles: chain ;
113              
114             chain: atom
115             {
116 759     759   37864 my $g = Graph::Undirected->new( refvertexed => 1 );
117 759         298440 $g->add_vertex( $_[1] );
118 759         61528 push @{$_[0]->{USER}{GRAPHS}}, $g;
  759         2881  
119              
120 759         1984 $_[1]->{graph} = $g;
121 759         1179 $_[1]->{index} = @{$_[0]->{USER}{GRAPHS}}-1;
  759         2542  
122              
123 759         3831 return { first => $_[1],
124             last => $_[1] };
125             }
126             | chain atom
127             {
128 700     700   32967 $_[2]->{graph} = $_[1]->{last}{graph};
129 700         1728 $_[2]->{index} = $_[1]->{last}{index};
130              
131 700         3357 $_[2]->{graph}->add_edge( $_[1]->{last}, $_[2] );
132              
133 700 100 100     219597 if( is_aromatic $_[1]->{last} && is_aromatic $_[2] ) {
134             $_[2]->{graph}->set_edge_attribute( $_[1]->{last},
135 161         6565 $_[2],
136             'bond',
137             ':' );
138             }
139              
140 700         56923 _push_chirality_neighbour( $_[1]->{last}, $_[2] );
141 700         2053 _push_chirality_neighbour( $_[2], $_[1]->{last} );
142              
143 700         1374 $_[1]->{last} = $_[2];
144              
145 700         1890 return $_[1];
146             }
147             | chain bond atom
148             {
149 203     203   8856 $_[3]->{graph} = $_[1]->{last}{graph};
150 203         529 $_[3]->{index} = $_[1]->{last}{index};
151              
152 203 100       961 if( $_[2] ne '-' ) {
153             $_[3]->{graph}->set_edge_attribute( $_[1]->{last},
154 196         6291 $_[3],
155             'bond',
156             $_[2] );
157             } else {
158 7         35 $_[3]->{graph}->add_edge( $_[1]->{last}, $_[3] );
159             }
160              
161 203         152393 _push_chirality_neighbour( $_[1]->{last}, $_[3] );
162 203         570 _push_chirality_neighbour( $_[3], $_[1]->{last} );
163              
164 203         407 $_[1]->{last} = $_[3];
165              
166 203         587 return $_[1];
167             }
168             | chain '.' atom
169             {
170 33     33   1513 my $g = Graph::Undirected->new( refvertexed => 1 );
171 33         5329 $g->add_vertex( $_[3] );
172 33         2233 push @{$_[0]->{USER}{GRAPHS}}, $g;
  33         107  
173              
174 33         91 $_[3]->{graph} = $g;
175 33         45 $_[3]->{index} = @{$_[0]->{USER}{GRAPHS}}-1;
  33         96  
176              
177 33         67 $_[1]->{last} = $_[3];
178              
179 33         74 return $_[1];
180             }
181             | chain '(' chain ')'
182             {
183 378 100 66 378   19221 if( $_[0]->{USER}{OPTIONS}{report_unnecessary_dot_usage} &&
184             $_[3]->{first}{index} != $_[3]->{last}{index} ) {
185 2         29 warn 'unnecessary use of dot in parenthesis' . "\n";
186             }
187              
188 378 100       1484 if( $_[1]->{last}{index} != $_[3]->{first}{index} ) {
189             $_[0]->_merge_graphs( $_[1]->{last}{index},
190 353         1511 $_[3]->{first}{index} );
191             }
192              
193 378         1998 $_[1]->{last}{graph}->add_edge( $_[1]->{last}, $_[3]->{first} );
194              
195 378 100 100     82231 if( is_aromatic $_[1]->{last} && is_aromatic $_[3]->{first} ) {
196             $_[1]->{last}{graph}->set_edge_attribute( $_[1]->{last},
197             $_[3]->{first},
198 25         807 'bond',
199             ':' );
200             }
201              
202 378         11836 _push_chirality_neighbour( $_[1]->{last}, $_[3]->{first} );
203 378         1317 _unshift_chirality_neighbour( $_[3]->{first}, $_[1]->{last} );
204              
205 378         1064 return $_[1];
206             }
207             | chain '(' bond chain ')'
208             {
209 46 100 66 46   2472 if( $_[0]->{USER}{OPTIONS}{report_unnecessary_dot_usage} &&
210             $_[4]->{first}{index} != $_[4]->{last}{index} ) {
211 1         15 warn 'unnecessary use of dot in parenthesis' . "\n";
212             }
213              
214 46 100       195 if( $_[1]->{last}{index} != $_[4]->{first}{index} ) {
215             $_[0]->_merge_graphs( $_[1]->{last}{index},
216 43         242 $_[4]->{first}{index} );
217             }
218              
219 46 100       171 if( $_[3] ne '-' ) {
220             $_[1]->{last}{graph}->set_edge_attribute( $_[1]->{last},
221             $_[4]->{first},
222 44         1404 'bond',
223             $_[3] );
224             } else {
225             $_[1]->{last}{graph}->add_edge( $_[1]->{last},
226 2         12 $_[4]->{first} );
227             }
228              
229 46         37010 _push_chirality_neighbour( $_[1]->{last}, $_[4]->{first} );
230 46         201 _unshift_chirality_neighbour( $_[4]->{first}, $_[1]->{last} );
231              
232 46         145 return $_[1];
233             }
234             | chain '(' '.' chain ')'
235             {
236 7 100   7   318 if( $_[0]->{USER}{OPTIONS}{report_unnecessary_dot_usage} ) {
237 4         43 warn 'unnecessary use of dot in parenthesis' . "\n";
238             }
239              
240 7         30 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 223     223   12117 $_[0]->_add_ring_bond( $_[1]->{last}, $_[2] );
250 222         563 return $_[1];
251             }
252             | chain bond ringbond
253             {
254 32     32   7842 $_[0]->_add_ring_bond( $_[1]->{last}, $_[3], $_[2] );
255 31         121 return $_[1];
256             }
257 180         21035 ;
258              
259             bond: '-' | '=' | '#' | '$' | ':' | '/' | '\\' ;
260              
261             %%
262              
263             # Footer section
264              
265             sub _Error
266             {
267 10     10   162 my( $self ) = @_;
268 10 50       24 close $self->{USER}{FILEIN} if $self->{USER}{FILEIN};
269              
270 10 100 100     13 if( ${$self->{TOKEN}} eq '' &&
  10         28  
271 7 100 100     43 grep { defined $_ && !ref $_ && $_ eq '(' }
272 7         11 map { $_->[1] } @{$self->{STACK}} ) {
  2         6  
273 1         138 die "$0: syntax error: missing closing parenthesis.\n";
274             }
275              
276 9 100       12 if( ${$self->{TOKEN}} eq ')' ) {
  9         18  
277 2         169 die "$0: syntax error: unbalanced parentheses.\n";
278             }
279              
280 7         30 my $msg = "$0: syntax error at position $self->{USER}{CHARNO}";
281 180 100       22752 if( $self->YYData->{INPUT} ) {
  7         13  
282 6         34 $self->YYData->{INPUT} =~ s/\n$//;
283 6         30 die "$msg: '" . $self->YYData->{INPUT} . "'.\n";
284             } else {
285 1         60 die "$msg.\n";
286             }
287             }
288              
289             sub _Lexer
290             {
291 3473     3473   131735 my( $self ) = @_;
292              
293             # If the line is empty and the input is originating from the file,
294             # another line is read.
295 3473 50 66     8925 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 3473 50       31543 if( $self->YYData->{INPUT} =~ s/^(\s+)// ) {
302 0         0 $self->{USER}{CHARNO} += length $1;
303             }
304              
305 3473         31309 my $hcount_re = 'H[0-9]?';
306 3473 100       14273 if( defined $self->{USER}{OPTIONS}{max_hydrogen_count_digits} ) {
307             $hcount_re = sprintf 'H[0-9]{0,%d}',
308 2         8 $self->{USER}{OPTIONS}{max_hydrogen_count_digits};
309             }
310              
311             # Bracket atoms
312 3473 100       6855 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 238         10835 my $atom = { %+, number => $self->{USER}{ATOMNO} };
324 238         988 $self->{USER}{ATOMNO} ++;
325 238         741 $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 238 100 100     3879 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         24 die "aromatic chemical element '$atom->{symbol}' is not allowed\n";
334             } elsif( $atom->{symbol} eq 'Ha' ||
335             !Chemistry::Elements->new( $atom->{symbol} ) ) {
336 5         278 die "chemical element with symbol '$atom->{symbol}' is unknown\n";
337             }
338              
339 231 100       21307 if( $atom->{charge} ) {
340 23         686 $atom->{charge} =~ s/^([-+])$/${1}1/;
341 23         107 $atom->{charge} =~ s/^([-+])\1$/${1}2/;
342 23         81 $atom->{charge} = int $atom->{charge};
343             }
344              
345 231 100       7533 if( $atom->{hcount} ) {
346 29         146 $atom->{hcount} =~ s/^H//;
347 29 100       112 $atom->{hcount} = $atom->{hcount} ? int $atom->{hcount} : 1;
348             } else {
349 202         575 $atom->{hcount} = 0;
350             }
351              
352 231 100       656 if( $atom->{isotope} ) {
353 6         12 $atom->{isotope} = int $atom->{isotope};
354             }
355              
356             # Atom class is an arbitrary number, 0 by default
357 231 100       747 $atom->{class} = exists $atom->{class} ? int $atom->{class} : 0;
358              
359 231         1160 return ( 'atom', $atom );
360             }
361              
362             # Bracketless atoms
363 3235 100       45672 if( $self->YYData->{INPUT} =~ s/^(Br|Cl|[BCINOPSFbcnops*])// ) {
364             my $atom = { symbol => $1,
365             class => 0,
366 1464         19985 number => $self->{USER}{ATOMNO} };
367 1464         2656 $self->{USER}{ATOMNO} ++;
368 1464         3386 $self->{USER}{CHARNO} += length $&;
369 1464         5942 return ( 'atom', $atom );
370             }
371              
372             # Ring bonds
373 1771 100 100     13757 if( $self->YYData->{INPUT} =~ s/^%([0-9]{2})// ||
374             $self->YYData->{INPUT} =~ s/^([0-9])// ) {
375 255         4365 $self->{USER}{CHARNO} += length $&;
376 255         1389 return ( 'ringbond', int $1 );
377             }
378              
379 1516         21533 my $char = substr( $self->YYData->{INPUT}, 0, 1 );
380 1516 100       11287 if( $char ne '' ) {
381 1196         2521 $self->YYData->{INPUT} = substr( $self->YYData->{INPUT}, 1 );
382             }
383 1516         12927 $self->{USER}{CHARNO} ++;
384 1516         5576 return( $char, $char );
385             }
386              
387             sub parse
388             {
389 337     337 1 203994 my( $self, $string, $options ) = @_;
390 337 100       1503 $options = {} unless $options;
391              
392 337         1625 $self->YYData->{INPUT} = $string;
393 337         6947 $self->{USER}{GRAPHS} = [];
394 337         1049 $self->{USER}{RINGBONDS} = {};
395 337         915 $self->{USER}{ATOMNO} = 0;
396 337         742 $self->{USER}{CHARNO} = 0;
397 337         865 $self->{USER}{OPTIONS} = $options;
398             $self->YYParse( yylex => \&_Lexer,
399             yyerror => \&_Error,
400 337         2733 yydebug => $options->{debug} );
401              
402 318 100       32720 if( scalar keys %{$self->{USER}{RINGBONDS}} ) {
  318         1553  
403             die "$0: unclosed ring bond(s) detected: " .
404 2         9 join( ', ', sort { $a <=> $b } keys %{$self->{USER}{RINGBONDS}} ) .
  1         53  
  2         113  
405             ".\n";
406             }
407              
408 316         600 my @graphs = grep { defined } @{$self->{USER}{GRAPHS}};
  780         1809  
  316         995  
409 316         794 for my $graph (@graphs) {
410 349         1285 for my $atom (sort { $a->{number} <=> $b->{number} } $graph->vertices) {
  2287         12405  
411 1674         4746 delete $atom->{graph};
412 1674         2549 delete $atom->{index};
413 1674 100       3852 next if $options->{raw};
414              
415             # Promote implicit hydrogen atoms into explicit ones
416 932 100       2176 if( !exists $atom->{hcount} ) {
417 778 100       3500 next if !exists $normal_valence{$atom->{symbol}};
418 1286 100 100     315085 my $degree = sum0 map { $_ ne ':' && exists $bond_symbol_to_order{$_} ? $bond_symbol_to_order{$_} : 1 }
419 776 100       2746 map { $graph->has_edge_attribute( $atom, $_, 'bond' )
  1286         393242  
420             ? $graph->get_edge_attribute( $atom, $_, 'bond' )
421             : '-' }
422             $graph->neighbours( $atom );
423 780     780   2455 my $valence = first { $degree <= $_ }
424 776         34091 @{$normal_valence{$atom->{symbol}}};
  776         3381  
425 776 100       4899 next unless defined $valence;
426 772         2118 $atom->{hcount} = $valence - $degree;
427             }
428 926         3000 for (1..$atom->{hcount}) {
429             my $hydrogen = { symbol => 'H',
430             class => 0,
431 909         3748 number => $self->{USER}{ATOMNO} };
432 909         3116 $graph->add_edge( $atom, $hydrogen );
433 909         206551 $self->{USER}{ATOMNO} ++;
434 909 100       2945 if( is_chiral $atom ) {
435 8 50       28 if( $atom->{chirality_neighbours} ) {
436 8 100   16   47 if( any { $_->{number} < $atom->{number} } @{$atom->{chirality_neighbours}} ) {
  16         47  
  8         41  
437 4         8 splice @{$atom->{chirality_neighbours}}, 1, 0, $hydrogen;
  4         29  
438             } else {
439 4         14 _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 926         1800 delete $atom->{hcount};
449              
450             # Unify the representation of chirality
451 926 100       2428 if( is_chiral $atom ) {
452 57 100       490 if( $atom->{chirality} =~ /^@@?$/ ) {
453 54 100       392 if( $graph->degree( $atom ) == 2 ) {
    100          
    100          
454 9         4953 $atom->{chirality} =~ s/@+/'@AL' . length $&/e;
  9         55  
455             } elsif( $graph->degree( $atom ) == 5 ) {
456 1         1887 $atom->{chirality} =~ s/@+/'@TB' . length $&/e;
  1         9  
457             } elsif( $graph->degree( $atom ) == 6 ) {
458 2         7952 $atom->{chirality} =~ s/@+/'@OH' . length $&/e;
  2         11  
459             }
460             }
461              
462 57         86747 $atom->{chirality} =~ s/^\@TH1$/@/;
463 57         196 $atom->{chirality} =~ s/^\@TH2$/@@/;
464             }
465             }
466             }
467              
468 316         2743 return @graphs;
469             }
470              
471             sub _add_ring_bond
472             {
473 255     255   827 my( $self, $atom, $ring_bond, $bond ) = @_;
474 255 100       924 if( $self->{USER}{RINGBONDS}{$ring_bond} ) {
475             $self->_merge_graphs( $self->{USER}{RINGBONDS}{$ring_bond}{atom}{index},
476 126         693 $atom->{index} );
477              
478 126 50 100     747 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         99 die "$0: ring bond types for ring bond $ring_bond do not match.\n";
486             }
487 234     234   576 $bond = first { defined }
488 125         1445 ( $self->{USER}{RINGBONDS}{$ring_bond}{bond}, $bond );
489              
490 125 100 100     816 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         15 $bond = toggle_cistrans $bond;
494             }
495              
496 125         310 my $ring_atom = $self->{USER}{RINGBONDS}{$ring_bond}{atom};
497 125 100       529 die "atom cannot be bonded to itself\n" if $atom == $ring_atom;
498 124 100 100     624 if( !$bond && is_aromatic $ring_atom && is_aromatic $atom ) {
      100        
499 36         116 $bond = ':';
500             }
501 124 100 100     625 if( $bond && $bond ne '-' ) {
502 51         1670 $atom->{graph}->set_edge_attribute( $ring_atom,
503             $atom,
504             'bond',
505             $bond );
506             } else {
507 73         384 $atom->{graph}->add_edge( $ring_atom, $atom );
508             }
509 124         42303 delete $self->{USER}{RINGBONDS}{$ring_bond};
510              
511 124 50 66     526 if( is_chiral $ring_atom && $ring_atom->{chirality_neighbours} ) {
512             my $pos = first { !ref $ring_atom->{chirality_neighbours}[$_] &&
513 7 100   7   70 $ring_atom->{chirality_neighbours}[$_] == $ring_bond }
514 4         25 0..$#{$ring_atom->{chirality_neighbours}};
  4         23  
515 4 50       29 $ring_atom->{chirality_neighbours}[$pos] = $atom if defined $pos;
516             }
517 124         411 _push_chirality_neighbour( $atom, $ring_atom );
518             } else {
519 129 100       653 $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 129         409 _push_chirality_neighbour( $atom, $ring_bond );
525             }
526             }
527              
528             sub _merge_graphs
529             {
530 522     522   1205 my( $self, $index1, $index2 ) = @_;
531 522 100       1437 return if $index1 == $index2;
532              
533 431         1014 my $g1 = $self->{USER}{GRAPHS}[$index1];
534 431         821 my $g2 = $self->{USER}{GRAPHS}[$index2];
535              
536 431         1620 for ($g2->vertices) {
537 947         13372 $_->{graph} = $g1;
538 947         1816 $_->{index} = $index1;
539             }
540 431         1100 $g1->add_vertices( $g2->vertices );
541              
542 431         54318 for ($g2->edges) {
543 529         148671 my $attributes = $g2->get_edge_attributes( @$_ );
544 529 100       161874 if( $attributes ) {
545 151         3196 $g1->set_edge_attributes( @$_, $attributes );
546             } else {
547 378         1239 $g1->add_edge( @$_ );
548             }
549             }
550              
551 431         45795 $self->{USER}{GRAPHS}[$index2] = undef;
552             }
553              
554             sub _push_chirality_neighbour
555             {
556 2483     2483   4652 my( $atom1, $atom2 ) = @_;
557 2483 100       5927 return unless is_chiral $atom1;
558 344         553 push @{$atom1->{chirality_neighbours}}, $atom2;
  344         1091  
559             }
560              
561             sub _unshift_chirality_neighbour
562             {
563 428     428   856 my( $atom1, $atom2 ) = @_;
564 428 100       945 return unless is_chiral $atom1;
565 7         20 unshift @{$atom1->{chirality_neighbours}}, $atom2;
  7         47  
566             }
567              
568             1;
569              
570             =head1 AUTHORS
571              
572             Andrius Merkys, Emerkys@cpan.orgE
573              
574             =cut