source: trunk/CifFile/YappsStarParser_STAR2.py @ 4082

Last change on this file since 4082 was 4082, checked in by vondreele, 4 years ago
File size: 20.8 KB
Line 
1# To maximize python3/python2 compatibility
2from __future__ import print_function
3from __future__ import unicode_literals
4from __future__ import division
5from __future__ import absolute_import
6
7from .StarFile import StarBlock,StarFile,StarList,StarDict
8from io import StringIO
9# An alternative specification for the Cif Parser, based on Yapps2
10# by Amit Patel (http://theory.stanford.edu/~amitp/Yapps)
11#
12# helper code: we define our match tokens
13lastval = ''
14def monitor(location,value):
15    global lastval
16    #print 'At %s: %s' % (location,repr(value))
17    lastval = repr(value)
18    return value
19
20# Strip extras gets rid of leading and trailing whitespace, and
21# semicolons.
22def stripextras(value):
23     from .StarFile import remove_line_folding, remove_line_prefix
24     # we get rid of semicolons and leading/trailing terminators etc.
25     import re
26     jj = re.compile("[\n\r\f \t\v]*")
27     semis = re.compile("[\n\r\f \t\v]*[\n\r\f]\n*;")
28     cut = semis.match(value)
29     if cut:        #we have a semicolon-delimited string
30          nv = value[cut.end():len(value)-2]
31          try:
32             if nv[-1]=='\r': nv = nv[:-1]
33          except IndexError:    #empty data value
34             pass
35          # apply protocols
36          nv = remove_line_prefix(nv)
37          nv = remove_line_folding(nv)
38          return nv
39     else:
40          cut = jj.match(value)
41          if cut:
42               return stripstring(value[cut.end():])
43          return value
44
45# helper function to get rid of inverted commas etc.
46
47def stripstring(value):
48     if value:
49         if value[0]== '\'' and value[-1]=='\'':
50           return value[1:-1]
51         if value[0]=='"' and value[-1]=='"':
52           return value[1:-1]
53     return value
54
55# helper function to get rid of triple quotes
56def striptriple(value):
57    if value:
58        if value[:3] == '"""' and value[-3:] == '"""':
59            return value[3:-3]
60        if value[:3] == "'''" and value[-3:] == "'''":
61            return value[3:-3]
62    return value
63
64# helper function to populate a StarBlock given a list of names
65# and values .
66#
67# Note that there may be an empty list at the very end of our itemlists,
68# so we remove that if necessary.
69#
70
71def makeloop(target_block,loopdata):
72    loop_seq,itemlists = loopdata
73    if itemlists[-1] == []: itemlists.pop(-1)
74    # print 'Making loop with %s' % repr(itemlists)
75    step_size = len(loop_seq)
76    for col_no in range(step_size):
77       target_block.AddItem(loop_seq[col_no], itemlists[col_no::step_size],precheck=True)
78    # print 'Makeloop constructed %s' % repr(loopstructure)
79    # now construct the loop
80    try:
81        target_block.CreateLoop(loop_seq)  #will raise ValueError on problem
82    except ValueError:
83        error_string =  'Incorrect number of loop values for loop containing %s' % repr(loop_seq)
84        print(error_string, file=sys.stderr)
85        raise ValueError(error_string)
86
87# return an object with the appropriate amount of nesting
88def make_empty(nestlevel):
89    gd = []
90    for i in range(1,nestlevel):
91        gd = [gd]
92    return gd
93
94# this function updates a dictionary first checking for name collisions,
95# which imply that the CIF is invalid.  We need case insensitivity for
96# names.
97
98# Unfortunately we cannot check loop item contents against non-loop contents
99# in a non-messy way during parsing, as we may not have easy access to previous
100# key value pairs in the context of our call (unlike our built-in access to all
101# previous loops).
102# For this reason, we don't waste time checking looped items against non-looped
103# names during parsing of a data block.  This would only match a subset of the
104# final items.   We do check against ordinary items, however.
105#
106# Note the following situations:
107# (1) new_dict is empty -> we have just added a loop; do no checking
108# (2) new_dict is not empty -> we have some new key-value pairs
109#
110def cif_update(old_dict,new_dict,loops):
111    old_keys = map(lambda a:a.lower(),old_dict.keys())
112    if new_dict != {}:    # otherwise we have a new loop
113        #print 'Comparing %s to %s' % (repr(old_keys),repr(new_dict.keys()))
114        for new_key in new_dict.keys():
115            if new_key.lower() in old_keys:
116                raise CifError("Duplicate dataname or blockname %s in input file" % new_key)
117            old_dict[new_key] = new_dict[new_key]
118#
119# this takes two lines, so we couldn't fit it into a one line execution statement...
120def order_update(order_array,new_name):
121    order_array.append(new_name)
122    return new_name
123
124# and finally...turn a sequence into a python dict (thanks to Stackoverflow)
125def pairwise(iterable):
126    it = iter(iterable)
127    while 1:
128        yield next(it), next(it)
129
130
131# Begin -- grammar generated by Yapps
132import sys, re
133from . import yapps3_compiled_rt as yappsrt
134
135class StarParserScanner(yappsrt.Scanner):
136    def __init__(self, *args,**kwargs):
137        patterns = [
138         ('":"', ':'),
139         ('","', ','),
140         ('([ \t\n\r](?!;))|[ \t]', '([ \t\n\r](?!;))|[ \t]'),
141         ('(#.*[\n\r](?!;))|(#.*)', '(#.*[\n\r](?!;))|(#.*)'),
142         ('LBLOCK', '(L|l)(O|o)(O|o)(P|p)_'),
143         ('GLOBAL', '(G|g)(L|l)(O|o)(B|b)(A|a)(L|l)_'),
144         ('STOP', '(S|s)(T|t)(O|o)(P|p)_'),
145         ('save_heading', u'(S|s)(A|a)(V|v)(E|e)_[][!%&\\(\\)*+,./:<=>?@0-9A-Za-z\\\\^`{}\\|~"#$\';_\xa0-\ud7ff\ue000-\ufdcf\ufdf0-\ufffd\U00010000-\U0001fffd\U00020000-\U0002fffd\U00030000-\U0003fffd\U00040000-\U0004fffd\U00050000-\U0005fffd\U00060000-\U0006fffd\U00070000-\U0007fffd\U00080000-\U0008fffd\U00090000-\U0009fffd\U000a0000-\U000afffd\U000b0000-\U000bfffd\U000c0000-\U000cfffd\U000d0000-\U000dfffd\U000e0000-\U000efffd\U000f0000-\U000ffffd\U00100000-\U0010fffd-]+'),
146         ('save_end', '(S|s)(A|a)(V|v)(E|e)_'),
147         ('data_name', u'_[][!%&\\(\\)*+,./:<=>?@0-9A-Za-z\\\\^`{}\\|~"#$\';_\xa0-\ud7ff\ue000-\ufdcf\ufdf0-\ufffd\U00010000-\U0001fffd\U00020000-\U0002fffd\U00030000-\U0003fffd\U00040000-\U0004fffd\U00050000-\U0005fffd\U00060000-\U0006fffd\U00070000-\U0007fffd\U00080000-\U0008fffd\U00090000-\U0009fffd\U000a0000-\U000afffd\U000b0000-\U000bfffd\U000c0000-\U000cfffd\U000d0000-\U000dfffd\U000e0000-\U000efffd\U000f0000-\U000ffffd\U00100000-\U0010fffd-]+'),
148         ('data_heading', u'(D|d)(A|a)(T|t)(A|a)_[][!%&\\(\\)*+,./:<=>?@0-9A-Za-z\\\\^`{}\\|~"#$\';_\xa0-\ud7ff\ue000-\ufdcf\ufdf0-\ufffd\U00010000-\U0001fffd\U00020000-\U0002fffd\U00030000-\U0003fffd\U00040000-\U0004fffd\U00050000-\U0005fffd\U00060000-\U0006fffd\U00070000-\U0007fffd\U00080000-\U0008fffd\U00090000-\U0009fffd\U000a0000-\U000afffd\U000b0000-\U000bfffd\U000c0000-\U000cfffd\U000d0000-\U000dfffd\U000e0000-\U000efffd\U000f0000-\U000ffffd\U00100000-\U0010fffd-]+'),
149         ('start_sc_line', '(\n|\r\n);([^\n\r])*(\r\n|\r|\n)+'),
150         ('sc_line_of_text', '[^;\r\n]([^\r\n])*(\r\n|\r|\n)+'),
151         ('end_sc_line', ';'),
152         ('c_c_b', '\\}'),
153         ('o_c_b', '\\{'),
154         ('c_s_b', '\\]'),
155         ('o_s_b', '\\['),
156         ('dat_val_internal_sq', '\\[([^\\s\\[\\]]*)\\]'),
157         ('triple_quote_data_value', '(?s)\'\'\'.*?\'\'\'|""".*?"""'),
158         ('single_quote_data_value', '\'([^\n\r\x0c\'])*\'+|"([^\n\r"])*"+'),
159         ('END', '$'),
160         ('data_value_1', '((?!(((S|s)(A|a)(V|v)(E|e)_[^\\s]*)|((G|g)(L|l)(O|o)(B|b)(A|a)(L|l)_[^\\s]*)|((S|s)(T|t)(O|o)(P|p)_[^\\s]*)|((D|d)(A|a)(T|t)(A|a)_[^\\s]*)))[^\\s"#$\',_\\{\\}\\[\\]][^\\s,\\{\\}\\[\\]]*)'),
161        ]
162        yappsrt.Scanner.__init__(self,patterns,['([ \t\n\r](?!;))|[ \t]', '(#.*[\n\r](?!;))|(#.*)'],*args,**kwargs)
163
164class StarParser(yappsrt.Parser):
165    Context = yappsrt.Context
166    def input(self, prepared, _parent=None):
167        _context = self.Context(_parent, self._scanner, self._pos, 'input', [prepared])
168        _token = self._peek('END', 'data_heading')
169        if _token == 'data_heading':
170            dblock = self.dblock(prepared, _context)
171            allblocks = prepared; allblocks.merge_fast(dblock)
172            while self._peek('END', 'data_heading') == 'data_heading':
173                dblock = self.dblock(prepared, _context)
174                allblocks.merge_fast(dblock)
175            if self._peek() not in ['END', 'data_heading']:
176                raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['END', 'data_heading']))
177            END = self._scan('END')
178        else: # == 'END'
179            END = self._scan('END')
180            allblocks = prepared
181        return allblocks
182
183    def dblock(self, prepared, _parent=None):
184        _context = self.Context(_parent, self._scanner, self._pos, 'dblock', [prepared])
185        data_heading = self._scan('data_heading')
186        heading = data_heading[5:];thisbc=StarFile(characterset='unicode',standard=prepared.standard);act_heading = thisbc.NewBlock(heading,StarBlock(overwrite=False));stored_block = thisbc[act_heading]
187        while self._peek('save_heading', 'LBLOCK', 'data_name', 'save_end', 'END', 'data_heading') in ['save_heading', 'LBLOCK', 'data_name']:
188            _token = self._peek('save_heading', 'LBLOCK', 'data_name')
189            if _token != 'save_heading':
190                dataseq = self.dataseq(stored_block, _context)
191            else: # == 'save_heading'
192                save_frame = self.save_frame(_context)
193                thisbc.merge_fast(save_frame,parent=stored_block)
194        if self._peek() not in ['save_heading', 'LBLOCK', 'data_name', 'save_end', 'END', 'data_heading']:
195            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['save_heading', 'LBLOCK', 'data_name', 'save_end', 'END', 'data_heading']))
196        stored_block.setmaxnamelength(stored_block.maxnamelength);return (monitor('dblock',thisbc))
197
198    def dataseq(self, starblock, _parent=None):
199        _context = self.Context(_parent, self._scanner, self._pos, 'dataseq', [starblock])
200        data = self.data(starblock, _context)
201        while self._peek('LBLOCK', 'data_name', 'save_heading', 'save_end', 'END', 'data_heading') in ['LBLOCK', 'data_name']:
202            data = self.data(starblock, _context)
203        if self._peek() not in ['LBLOCK', 'data_name', 'save_heading', 'save_end', 'END', 'data_heading']:
204            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['LBLOCK', 'data_name', 'save_heading', 'save_end', 'END', 'data_heading']))
205
206    def data(self, currentblock, _parent=None):
207        _context = self.Context(_parent, self._scanner, self._pos, 'data', [currentblock])
208        _token = self._peek('LBLOCK', 'data_name')
209        if _token == 'LBLOCK':
210            top_loop = self.top_loop(_context)
211            makeloop(currentblock,top_loop)
212        else: # == 'data_name'
213            datakvpair = self.datakvpair(_context)
214            currentblock.AddItem(datakvpair[0],datakvpair[1],precheck=False)
215
216    def datakvpair(self, _parent=None):
217        _context = self.Context(_parent, self._scanner, self._pos, 'datakvpair', [])
218        data_name = self._scan('data_name')
219        data_value = self.data_value(_context)
220        return [data_name,data_value]
221
222    def data_value(self, _parent=None):
223        _context = self.Context(_parent, self._scanner, self._pos, 'data_value', [])
224        _token = self._peek('data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b')
225        if _token == 'data_value_1':
226            data_value_1 = self._scan('data_value_1')
227            thisval = data_value_1
228        elif _token not in ['start_sc_line', 'o_s_b', 'o_c_b']:
229            delimited_data_value = self.delimited_data_value(_context)
230            thisval = delimited_data_value
231        elif _token == 'start_sc_line':
232            sc_lines_of_text = self.sc_lines_of_text(_context)
233            thisval = stripextras(sc_lines_of_text)
234        else: # in ['o_s_b', 'o_c_b']
235            bracket_expression = self.bracket_expression(_context)
236            thisval = bracket_expression
237        return monitor('data_value',thisval)
238
239    def delimited_data_value(self, _parent=None):
240        _context = self.Context(_parent, self._scanner, self._pos, 'delimited_data_value', [])
241        _token = self._peek('triple_quote_data_value', 'single_quote_data_value')
242        if _token == 'triple_quote_data_value':
243            triple_quote_data_value = self._scan('triple_quote_data_value')
244            thisval = striptriple(triple_quote_data_value)
245        else: # == 'single_quote_data_value'
246            single_quote_data_value = self._scan('single_quote_data_value')
247            thisval = stripstring(single_quote_data_value)
248        return thisval
249
250    def sc_lines_of_text(self, _parent=None):
251        _context = self.Context(_parent, self._scanner, self._pos, 'sc_lines_of_text', [])
252        start_sc_line = self._scan('start_sc_line')
253        lines = StringIO();lines.write(start_sc_line)
254        while self._peek('end_sc_line', 'sc_line_of_text') == 'sc_line_of_text':
255            sc_line_of_text = self._scan('sc_line_of_text')
256            lines.write(sc_line_of_text)
257        if self._peek() not in ['end_sc_line', 'sc_line_of_text']:
258            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['sc_line_of_text', 'end_sc_line']))
259        end_sc_line = self._scan('end_sc_line')
260        lines.write(end_sc_line);return monitor('sc_line_of_text',lines.getvalue())
261
262    def bracket_expression(self, _parent=None):
263        _context = self.Context(_parent, self._scanner, self._pos, 'bracket_expression', [])
264        _token = self._peek('o_s_b', 'o_c_b')
265        if _token == 'o_s_b':
266            square_bracket_expr = self.square_bracket_expr(_context)
267            return square_bracket_expr
268        else: # == 'o_c_b'
269            curly_bracket_expr = self.curly_bracket_expr(_context)
270            return curly_bracket_expr
271
272    def top_loop(self, _parent=None):
273        _context = self.Context(_parent, self._scanner, self._pos, 'top_loop', [])
274        LBLOCK = self._scan('LBLOCK')
275        loopfield = self.loopfield(_context)
276        loopvalues = self.loopvalues(_context)
277        return loopfield,loopvalues
278
279    def loopfield(self, _parent=None):
280        _context = self.Context(_parent, self._scanner, self._pos, 'loopfield', [])
281        loop_seq=[]
282        while self._peek('data_name', 'data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b') == 'data_name':
283            data_name = self._scan('data_name')
284            loop_seq.append(data_name)
285        if self._peek() not in ['data_name', 'data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b']:
286            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['data_name', 'data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b']))
287        return loop_seq
288
289    def loopvalues(self, _parent=None):
290        _context = self.Context(_parent, self._scanner, self._pos, 'loopvalues', [])
291        data_value = self.data_value(_context)
292        dataloop=[data_value]
293        while self._peek('data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b', 'LBLOCK', 'data_name', 'save_heading', 'save_end', 'END', 'data_heading') in ['data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b']:
294            data_value = self.data_value(_context)
295            dataloop.append(monitor('loopval',data_value))
296        if self._peek() not in ['data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b', 'LBLOCK', 'data_name', 'save_heading', 'save_end', 'END', 'data_heading']:
297            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['data_value_1', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b', 'LBLOCK', 'data_name', 'save_heading', 'save_end', 'END', 'data_heading']))
298        return dataloop
299
300    def save_frame(self, _parent=None):
301        _context = self.Context(_parent, self._scanner, self._pos, 'save_frame', [])
302        save_heading = self._scan('save_heading')
303        savehead = save_heading[5:];savebc = StarFile();newname = savebc.NewBlock(savehead,StarBlock(overwrite=False));stored_block = savebc[newname]
304        while self._peek('save_end', 'save_heading', 'LBLOCK', 'data_name', 'END', 'data_heading') in ['save_heading', 'LBLOCK', 'data_name']:
305            _token = self._peek('save_heading', 'LBLOCK', 'data_name')
306            if _token != 'save_heading':
307                dataseq = self.dataseq(savebc[savehead], _context)
308            else: # == 'save_heading'
309                save_frame = self.save_frame(_context)
310                savebc.merge_fast(save_frame,parent=stored_block)
311        if self._peek() not in ['save_end', 'save_heading', 'LBLOCK', 'data_name', 'END', 'data_heading']:
312            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['save_end', 'save_heading', 'LBLOCK', 'data_name', 'END', 'data_heading']))
313        save_end = self._scan('save_end')
314        return monitor('save_frame',savebc)
315
316    def square_bracket_expr(self, _parent=None):
317        _context = self.Context(_parent, self._scanner, self._pos, 'square_bracket_expr', [])
318        o_s_b = self._scan('o_s_b')
319        this_list = []
320        while self._peek('c_s_b', 'data_value_1', '","', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b') not in ['c_s_b', '","']:
321            data_value = self.data_value(_context)
322            this_list.append(data_value)
323            while self._peek('","', 'data_value_1', 'c_s_b', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b') == '","':
324                self._scan('","')
325                data_value = self.data_value(_context)
326                this_list.append(data_value)
327            if self._peek() not in ['","', 'data_value_1', 'c_s_b', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b']:
328                raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['","', 'data_value_1', 'c_s_b', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b']))
329        if self._peek() not in ['c_s_b', 'data_value_1', '","', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', 'o_s_b', 'o_c_b']:
330            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['data_value_1', 'c_s_b', 'triple_quote_data_value', 'single_quote_data_value', 'start_sc_line', '","', 'o_s_b', 'o_c_b']))
331        c_s_b = self._scan('c_s_b')
332        return StarList(this_list)
333
334    def curly_bracket_expr(self, _parent=None):
335        _context = self.Context(_parent, self._scanner, self._pos, 'curly_bracket_expr', [])
336        o_c_b = self._scan('o_c_b')
337        table_as_list = []
338        while self._peek('c_c_b', 'triple_quote_data_value', 'single_quote_data_value', '","') in ['triple_quote_data_value', 'single_quote_data_value']:
339            delimited_data_value = self.delimited_data_value(_context)
340            table_as_list = [delimited_data_value]
341            self._scan('":"')
342            data_value = self.data_value(_context)
343            table_as_list.append(data_value)
344            while self._peek('","', 'triple_quote_data_value', 'single_quote_data_value', 'c_c_b') == '","':
345                self._scan('","')
346                delimited_data_value = self.delimited_data_value(_context)
347                table_as_list.append(delimited_data_value)
348                self._scan('":"')
349                data_value = self.data_value(_context)
350                table_as_list.append(data_value)
351            if self._peek() not in ['","', 'triple_quote_data_value', 'single_quote_data_value', 'c_c_b']:
352                raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['","', 'triple_quote_data_value', 'single_quote_data_value', 'c_c_b']))
353        if self._peek() not in ['c_c_b', 'triple_quote_data_value', 'single_quote_data_value', '","']:
354            raise yappsrt.SyntaxError(charpos=self._scanner.get_prev_char_pos(), context=_context, msg='Need one of ' + ', '.join(['triple_quote_data_value', 'single_quote_data_value', 'c_c_b', '","']))
355        c_c_b = self._scan('c_c_b')
356        return StarDict(pairwise(table_as_list))
357
358
359def parse(rule, text):
360    P = StarParser(StarParserScanner(text))
361    return yappsrt.wrap_error_reporter(P, rule)
362
363# End -- grammar generated by Yapps
364
365
Note: See TracBrowser for help on using the repository browser.