AtomSelectionParser.py 5.88 KB
Newer Older
eric pellegrini's avatar
test    
eric pellegrini committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#MDANSE : Molecular Dynamics Analysis for Neutron Scattering Experiments
#------------------------------------------------------------------------------------------
#Copyright (C)
#2015- Eric C. Pellegrini Institut Laue-Langevin
#BP 156
#6, rue Jules Horowitz
#38042 Grenoble Cedex 9
#France
#pellegrini[at]ill.fr
#goret[at]ill.fr
#aoun[at]ill.fr
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

''' 
Created on Mar 27, 2015

30
:author: Eric C. Pellegrini
eric pellegrini's avatar
test    
eric pellegrini committed
31
32
33
34
35
36
37
38
39
40
41
'''

import operator

from MDANSE import REGISTRY
from MDANSE.Core.Error import Error
from MDANSE.Externals.pyparsing.pyparsing import delimitedList, oneOf, opAssoc, operatorPrecedence, printables, Forward, OneOrMore, Optional, Word

class AtomSelectionParserError(Error):
    pass

42
class AtomSelectionParser(object):
eric pellegrini's avatar
test    
eric pellegrini committed
43
        
44
    def __init__(self, universe):
eric pellegrini's avatar
test    
eric pellegrini committed
45
        
46
        self._universe = universe
eric pellegrini's avatar
test    
eric pellegrini committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
                
    def operator_and(self, token):
        
        token[0][1] = "&"
        
        return " ".join(token[0])

    def operator_not(self, token):
                                
        return 'REGISTRY[%r]["all"](universe).select() - %s' % ("selector",token[0][1])

    def operator_or(self, token):
        
        token[0][1] = "|"
            
        return " ".join(token[0])
    
    def parse_arguments(self,token):

66
        return "(%s) " % str(token)
eric pellegrini's avatar
test    
eric pellegrini committed
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

    def parse_expression(self, token):
                        
        return "".join([str(t) for t in token])
    
    def parse_keyword(self, token):
                            
        return 'REGISTRY[%r]["%s"](universe).select' % ("selector",token[0])
    
    def parse_selection_expression(self, expression):
                
        expression = expression.replace("(","( ")
        expression = expression.replace(")"," )")
        
        linkers   = oneOf(["and","&","or","|","not","~"], caseless=True)
        keyword   = oneOf(REGISTRY["selector"].keys(), caseless=True).setParseAction(self.parse_keyword)
        arguments = Optional(~linkers + delimitedList(Word(printables,excludeChars=","),combine=False)).setParseAction(self.parse_arguments)
        
        selector = OneOrMore((keyword+arguments))
        
        grammar = Forward()
        
        grammar << selector.setParseAction(self.parse_expression)
90
91
        
        grammar = operatorPrecedence(grammar, [(oneOf(["and","&"],caseless=True), 2, opAssoc.RIGHT , self.operator_and),
eric pellegrini's avatar
test    
eric pellegrini committed
92
                                               (oneOf(["not","~"],caseless=True), 1, opAssoc.RIGHT, self.operator_not),
93
                                               (oneOf(["or","|"] ,caseless=True), 2, opAssoc.RIGHT , self.operator_or)],
eric pellegrini's avatar
test    
eric pellegrini committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
                                     lpar="(",
                                     rpar=")")

        try:          
            parsedExpression = grammar.transformString(expression)
            namespace={"REGISTRY":REGISTRY,"universe":self._universe}
            selection = eval(parsedExpression,namespace)
        except:
            raise AtomSelectionParserError("%r is not a valid selection string expression" % expression)
        
        selection = sorted(selection, key=operator.attrgetter("index"))
                                
        return selection 
                                                                                                        
108
    def parse(self, expression):
eric pellegrini's avatar
test    
eric pellegrini committed
109
110
111
112
113
114
115
                                    
        # Perfom the actual selection.
        selection = self.parse_selection_expression(expression)
                            
        if not selection:
            raise AtomSelectionParserError("No atoms matched the selection %r." % expression)
        
116
        indexes = [at.index for at in selection]
eric pellegrini's avatar
test    
eric pellegrini committed
117
                
118
119
120
121
122
123
124
        return indexes
    
if __name__ == "__main__":

    from MDANSE.Externals.pyparsing.pyparsing import *

    def parse_keyword(token):
eric pellegrini's avatar
eric pellegrini committed
125
                                    
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
        return '"%s"(universe).select' % token[0]

    def parse_arguments(token):

        return "(%s)" % str(token)

    def operator_and(token):
        
        token[0][1] = "&"
        
        return " ".join(token[0])

    def operator_or(token):
        
        token[0][1] = "|"
            
        return " ".join(token[0])

    def parse_expression(self, token):
                        
        return "".join([str(t) for t in token])
        
    expression = 'carbo * or oxy * or nitro *'
    
    linkers   = oneOf(["and","or",], caseless=True)
    keyword   = oneOf(['carbo','oxy','nitro'], caseless=True).setParseAction(parse_keyword)
    arguments = Optional(~linkers + delimitedList(Word(printables,excludeChars=","),combine=False)).setParseAction(parse_arguments)
    
    selector = keyword+arguments
    
    grammar = Forward()
    
    grammar << selector.setParseAction(parse_expression)
    
    expr = operatorPrecedence(grammar, [(oneOf(["and"],caseless=True), 2, opAssoc.RIGHT , operator_and),
                                           (oneOf(["or"] ,caseless=True), 2, opAssoc.RIGHT , operator_or)],
                                 lpar="(",
                                 rpar=")")

    parsedExpression = expr.transformString(expression)
eric pellegrini's avatar
eric pellegrini committed
166