Skip to content

Commit

Permalink
Support isotopes in logical expressions. The grammar needed to be cha…
Browse files Browse the repository at this point in the history
…nged and introduces and ambiguity but does the correct thing, we use LOOKAHEAD(1) to suppress the warnings.
  • Loading branch information
johnmay committed Dec 15, 2017
1 parent 6bb9541 commit b59c225
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 22 deletions.
Expand Up @@ -315,7 +315,7 @@ TOKEN_MGR_DECLS : {
* ImplicitHighAndExpression ::= <NotExpression> ( <ImplicitHighAndExpression> ) ?
* NotExpression ::= "!" ( <PrimitiveAtomExpression> | <RecursiveSmartsExpression> )
* RecursiveSmartsExpression ::= "$" "(" <SmartsExpression> ")"
* PrimitiveAtomExpression ::= <NonHydrogenElement> | "*" | "A" | "a" | "D" (<Digits>)? | "H" (<Digits>)? | "h" (<Digits>)?
* PrimitiveAtomExpression ::= <AtomicMass> | <NonHydrogenElement> | "*" | "A" | "a" | "D" (<Digits>)? | "H" (<Digits>)? | "h" (<Digits>)?
* | "R" (<Digit>+)? | "r" (<Digit>+)? | "v" (<Digit>+)? | "#X" | "G" (<DIGIT>+)
* | "X" (<Digit>+)? | "x" (<Digit>+)? | "^" (<DIGIT>)
* | ("+" | "-") (<Digit>+)? | "#" (<Digit>+) | "@" | "@@" | <Digit>+
Expand Down Expand Up @@ -447,15 +447,14 @@ ASTAtom AtomExpression() #Atom :
}
{
(
(

(
<L_BRACKET> { token_source.SwitchTo(SMARTSParserConstants.ATOM_EXPRESSION);
firstToken = getToken(1);
secondToken = getToken(2); }
( AtomicMass() { massNode = (ASTAtomicMass)jjtree.popNode(); } )?
LowAndExpression()
( LOOKAHEAD(2) AtomicMass() { massNode = (ASTAtomicMass)jjtree.popNode(); } )?
LowAndExpression()
{
if (massNode != null) { // insert AtomicMass node into expression
if (massNode != null) { // insert AtomicMass node into expression
ASTLowAndExpression topNode = (ASTLowAndExpression)jjtree.popNode();
topNode.insertLeafChild(massNode);
jjtree.pushNode(topNode);
Expand Down Expand Up @@ -600,8 +599,7 @@ void RecursiveSmartsExpression() #RecursiveSmartsExpression : {}

void PrimitiveAtomExpression() : {}
{
(
NoHydrogenElement()
( NoHydrogenElement()
|
AnyAtom()
|
Expand Down Expand Up @@ -636,35 +634,37 @@ void PrimitiveAtomExpression() : {}
NonCHHeavyAtom()
|
HybridizationNumber()
|
AtomicMass()
)
}

void TotalHCount() #TotalHCount :
{ StringBuilder digits = new StringBuilder(); }
{
<H> { jjtThis.setCount(1); } [ ( <DIGIT> { digits.append(token.image); } )+
<H> { jjtThis.setCount(1); } [ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setCount( Integer.parseInt(digits.toString()) ); } ]
}

void ImplicitHCount() #ImplicitHCount:
{ StringBuilder digits = new StringBuilder(); }
{
<h> { jjtThis.setCount(1); } [ ( <DIGIT> { digits.append(token.image); } )+
<h> { jjtThis.setCount(1); } [ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setCount( Integer.parseInt(digits.toString()) ); } ]
}

void ExplicitConnectivity() #ExplicitConnectivity :
{ StringBuilder digits = new StringBuilder(); }
{
<D> { jjtThis.setNumOfConnection(1); }
[ ( <DIGIT> { digits.append(token.image); } )+
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setNumOfConnection( Integer.parseInt(digits.toString()) ); } ]
}

void AtomicNumber() #AtomicNumber :
{ StringBuilder digits = new StringBuilder(); }
{
"#" ( <DIGIT> { digits.append(token.image); } )+
"#" ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setNumber( Integer.parseInt(digits.toString()) ); }
}

Expand All @@ -685,10 +685,10 @@ void Charge() #Charge :
// with more than 8 of them
LOOKAHEAD(2)
"+" { jjtThis.setPositive(true); jjtThis.setCharge(1); }
[ ( <DIGIT> { digits.append(token.image); } )+
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setCharge( Integer.parseInt(digits.toString()) ); } ]
| "-" { jjtThis.setPositive(false); jjtThis.setCharge(1); }
[ ( <DIGIT> { digits.append(token.image); } )+
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setCharge( Integer.parseInt(digits.toString()) ); } ]
| "--" { jjtThis.setPositive(false); jjtThis.setCharge(2); }
| "---" { jjtThis.setPositive(false); jjtThis.setCharge(3); }
Expand All @@ -709,14 +709,14 @@ void Charge() #Charge :
void RingConnectivity() #RingConnectivity : {}
{
<x> { jjtThis.setNumOfConnection(1); }
[ ( <DIGIT> )+ { jjtThis.setNumOfConnection( Integer.parseInt(token.image) ); } ]
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> )+ { jjtThis.setNumOfConnection( Integer.parseInt(token.image) ); } ]
}

void PeriodicGroupNumber() throws ParseException #PeriodicGroupNumber :
{ StringBuilder digits = new StringBuilder(); }
{
<G> { }
( <DIGIT> { digits.append(token.image); } )+
( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ int tmpInt = Integer.parseInt(digits.toString());
if (tmpInt < 1 || tmpInt > 18) throw new ParseException("Invalid group number");
jjtThis.setGroupNumber( Integer.parseInt(digits.toString()) ); }
Expand All @@ -727,31 +727,31 @@ void TotalConnectivity() #TotalConnectivity :
{ StringBuilder digits = new StringBuilder(); }
{
<X> { jjtThis.setNumOfConnection(1); }
[ ( <DIGIT> { digits.append(token.image); } )+
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setNumOfConnection( Integer.parseInt(digits.toString()) ); } ]
}

void Valence() #Valence :
{ StringBuilder digits = new StringBuilder(); }
{
<v> { jjtThis.setOrder(1); }
[ ( <DIGIT> { digits.append(token.image); })+
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); })+
{ jjtThis.setOrder( Integer.parseInt(digits.toString()) ); } ]
}

void RingMembership() #RingMembership :
{ StringBuilder digits = new StringBuilder(); }
{
<R> { jjtThis.setNumOfMembership(-1); }
[ ( <DIGIT> { digits.append(token.image); } )+
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setNumOfMembership( Integer.parseInt(digits.toString()) ); } ]
}

void SmallestRingSize() #SmallestRingSize :
{ StringBuilder digits = new StringBuilder(); }
{
<r> { jjtThis.setSize(-1); }
[ ( <DIGIT> { digits.append(token.image); } )+
[ LOOKAHEAD(1) ( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{ jjtThis.setSize( Integer.parseInt(digits.toString()) ); } ]
}

Expand All @@ -778,7 +778,7 @@ void AnyAtom() #AnyAtom : {}
void AtomicMass() #AtomicMass :
{ StringBuilder digits = new StringBuilder(); }
{
( <DIGIT> { digits.append(token.image); } )+
( LOOKAHEAD(1) <DIGIT> { digits.append(token.image); } )+
{
jjtThis.setMass( Integer.parseInt(digits.toString()) );
}
Expand All @@ -793,7 +793,7 @@ void Chirality() #Chirality :
{ StringBuilder digits = new StringBuilder(); }
{
"@" { jjtThis.setClockwise(false); }
(
( LOOKAHEAD(1)
"@" { jjtThis.setClockwise(true); }
)?
(
Expand Down
Expand Up @@ -33,17 +33,26 @@
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IBond.Order;
import org.openscience.cdk.interfaces.IChemObjectBuilder;
import org.openscience.cdk.isomorphism.matchers.IQueryAtom;
import org.openscience.cdk.isomorphism.matchers.OrderQueryBond;
import org.openscience.cdk.isomorphism.matchers.QueryAtomContainer;
import org.openscience.cdk.isomorphism.matchers.SymbolAndChargeQueryAtom;
import org.openscience.cdk.isomorphism.matchers.SymbolQueryAtom;
import org.openscience.cdk.isomorphism.matchers.smarts.AnyAtom;
import org.openscience.cdk.isomorphism.matchers.smarts.AnyOrderQueryBond;
import org.openscience.cdk.isomorphism.matchers.smarts.ImplicitHCountAtom;
import org.openscience.cdk.isomorphism.matchers.smarts.LogicalOperatorAtom;
import org.openscience.cdk.isomorphism.matchers.smarts.MassAtom;
import org.openscience.cdk.isomorphism.matchers.smarts.SMARTSAtom;
import org.openscience.cdk.silent.SilentChemObjectBuilder;
import org.openscience.cdk.smiles.SmilesParser;
import org.openscience.cdk.smiles.smarts.parser.SMARTSParser;
import org.openscience.cdk.templates.TestMoleculeFactory;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

/**
* @cdk.module test-smarts
* @cdk.require java1.4+
Expand Down Expand Up @@ -160,4 +169,15 @@ public void testMatchInherited() {
}

}

@Test public void testUnspecifiedIsotope() {
IAtom aexpr = SMARTSParser.parse("[!0]", SilentChemObjectBuilder.getInstance())
.getAtom(0);
assertThat(aexpr, instanceOf(LogicalOperatorAtom.class));
assertThat(((LogicalOperatorAtom)aexpr).getOperator(),
is("not"));
IQueryAtom subexpr = ((LogicalOperatorAtom) aexpr).getLeft();
assertThat(subexpr, instanceOf(MassAtom.class));
assertThat(subexpr.getMassNumber(), is(0));
}
}
Expand Up @@ -232,6 +232,22 @@ public void stereo_ring_closures() throws Exception {
assertTrue(ptrn.matches(smi("[C@@]1(O[C@@]([C@@]([C@]([C@]1(C)O)(C)O)(O)C)(O)C)(O)C")));
}

@Test
public void hasIsotope() throws Exception {
Pattern ptrn = SmartsPattern.create("[!0]");
assertFalse(ptrn.matches(smi("C")));
assertTrue(ptrn.matches(smi("[12C]")));
assertTrue(ptrn.matches(smi("[13C]")));
}

@Test
public void hIsotope() throws Exception {
Pattern ptrn = SmartsPattern.create("[2#1,3#1]");
assertFalse(ptrn.matches(smi("[H][H]")));
assertTrue(ptrn.matches(smi("[2H]")));
assertTrue(ptrn.matches(smi("[3H]")));
}

IAtomContainer smi(String smi) throws Exception {
return new SmilesParser(bldr).parseSmiles(smi);
}
Expand Down

0 comments on commit b59c225

Please sign in to comment.