from collections import defaultdict
import re
[docs]class TreeNode(object):
def __init__(self, text, offset, elements=None):
self.text = text
self.offset = offset
self.elements = elements or []
def __iter__(self):
for el in self.elements:
yield el
[docs]class TreeNode1(TreeNode):
def __init__(self, text, offset, elements):
super(TreeNode1, self).__init__(text, offset, elements)
self.complex = elements[0]
[docs]class TreeNode2(TreeNode):
def __init__(self, text, offset, elements):
super(TreeNode2, self).__init__(text, offset, elements)
self.complex = elements[1]
[docs]class TreeNode3(TreeNode):
def __init__(self, text, offset, elements):
super(TreeNode3, self).__init__(text, offset, elements)
self.fragment = elements[1]
[docs]class TreeNode4(TreeNode):
def __init__(self, text, offset, elements):
super(TreeNode4, self).__init__(text, offset, elements)
self.element = elements[0]
self.num = elements[1]
[docs]class TreeNode5(TreeNode):
def __init__(self, text, offset, elements):
super(TreeNode5, self).__init__(text, offset, elements)
self.sign = elements[0]
self.complex = elements[1]
[docs]class ParseError(SyntaxError):
pass
FAILURE = object()
[docs]class Grammar(object):
REGEX_1 = re.compile('^[+ -]')
REGEX_2 = re.compile('^[A-Z]')
REGEX_3 = re.compile('^[a-z]')
REGEX_4 = re.compile('^[0-9]')
def _read_formula(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['formula'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
index1, elements0 = self._offset, []
address1 = FAILURE
address1 = self._read_complex()
if address1 is not FAILURE:
elements0.append(address1)
address2 = FAILURE
remaining0, index2, elements1, address3 = 0, self._offset, [], True
while address3 is not FAILURE:
index3, elements2 = self._offset, []
address4 = FAILURE
chunk0 = None
if self._offset < self._input_size:
chunk0 = self._input[self._offset:self._offset + 1]
if chunk0 == '.':
address4 = TreeNode(self._input[self._offset:self._offset + 1], self._offset)
self._offset = self._offset + 1
else:
address4 = FAILURE
if self._offset > self._failure:
self._failure = self._offset
self._expected = []
if self._offset == self._failure:
self._expected.append('"."')
if address4 is not FAILURE:
elements2.append(address4)
address5 = FAILURE
address5 = self._read_complex()
if address5 is not FAILURE:
elements2.append(address5)
else:
elements2 = None
self._offset = index3
else:
elements2 = None
self._offset = index3
if elements2 is None:
address3 = FAILURE
else:
address3 = TreeNode2(self._input[index3:self._offset], index3, elements2)
self._offset = self._offset
if address3 is not FAILURE:
elements1.append(address3)
remaining0 -= 1
if remaining0 <= 0:
address2 = TreeNode(self._input[index2:self._offset], index2, elements1)
self._offset = self._offset
else:
address2 = FAILURE
if address2 is not FAILURE:
elements0.append(address2)
address6 = FAILURE
remaining1, index4, elements3, address7 = 0, self._offset, [], True
while address7 is not FAILURE:
address7 = self._read_adduct()
if address7 is not FAILURE:
elements3.append(address7)
remaining1 -= 1
if remaining1 <= 0:
address6 = TreeNode(self._input[index4:self._offset], index4, elements3)
self._offset = self._offset
else:
address6 = FAILURE
if address6 is not FAILURE:
elements0.append(address6)
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
if elements0 is None:
address0 = FAILURE
else:
address0 = self._actions.make_formula(self._input, index1, self._offset, elements0)
self._offset = self._offset
self._cache['formula'][index0] = (address0, self._offset)
return address0
def _read_complex(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['complex'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
index1, elements0 = self._offset, []
address1 = FAILURE
index2 = self._offset
address1 = self._read_number()
if address1 is FAILURE:
address1 = TreeNode(self._input[index2:index2], index2)
self._offset = index2
if address1 is not FAILURE:
elements0.append(address1)
address2 = FAILURE
remaining0, index3, elements1, address3 = 1, self._offset, [], True
while address3 is not FAILURE:
address3 = self._read_fragment()
if address3 is not FAILURE:
elements1.append(address3)
remaining0 -= 1
if remaining0 <= 0:
address2 = TreeNode(self._input[index3:self._offset], index3, elements1)
self._offset = self._offset
else:
address2 = FAILURE
if address2 is not FAILURE:
elements0.append(address2)
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
if elements0 is None:
address0 = FAILURE
else:
address0 = self._actions.expand_complex(self._input, index1, self._offset, elements0)
self._offset = self._offset
self._cache['complex'][index0] = (address0, self._offset)
return address0
def _read_fragment(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['fragment'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
index1 = self._offset
address0 = self._read_non_expanded_fragment()
if address0 is FAILURE:
self._offset = index1
address0 = self._read_expanded_fragment()
if address0 is FAILURE:
self._offset = index1
self._cache['fragment'][index0] = (address0, self._offset)
return address0
def _read_non_expanded_fragment(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['non_expanded_fragment'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
index1, elements0 = self._offset, []
address1 = FAILURE
chunk0 = None
if self._offset < self._input_size:
chunk0 = self._input[self._offset:self._offset + 1]
if chunk0 == '(':
address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset)
self._offset = self._offset + 1
else:
address1 = FAILURE
if self._offset > self._failure:
self._failure = self._offset
self._expected = []
if self._offset == self._failure:
self._expected.append('"("')
if address1 is not FAILURE:
elements0.append(address1)
address2 = FAILURE
address2 = self._read_fragment()
if address2 is not FAILURE:
elements0.append(address2)
address3 = FAILURE
chunk1 = None
if self._offset < self._input_size:
chunk1 = self._input[self._offset:self._offset + 1]
if chunk1 == ')':
address3 = TreeNode(self._input[self._offset:self._offset + 1], self._offset)
self._offset = self._offset + 1
else:
address3 = FAILURE
if self._offset > self._failure:
self._failure = self._offset
self._expected = []
if self._offset == self._failure:
self._expected.append('")"')
if address3 is not FAILURE:
elements0.append(address3)
address4 = FAILURE
index2 = self._offset
address4 = self._read_number()
if address4 is FAILURE:
address4 = TreeNode(self._input[index2:index2], index2)
self._offset = index2
if address4 is not FAILURE:
elements0.append(address4)
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
if elements0 is None:
address0 = FAILURE
else:
address0 = self._actions.expand_fragment(self._input, index1, self._offset, elements0)
self._offset = self._offset
self._cache['non_expanded_fragment'][index0] = (address0, self._offset)
return address0
def _read_expanded_fragment(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['expanded_fragment'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
remaining0, index1, elements0, address1 = 1, self._offset, [], True
while address1 is not FAILURE:
address1 = self._read_simple_fragment()
if address1 is not FAILURE:
elements0.append(address1)
remaining0 -= 1
if remaining0 <= 0:
address0 = self._actions.combine_fragments(self._input, index1, self._offset, elements0)
self._offset = self._offset
else:
address0 = FAILURE
self._cache['expanded_fragment'][index0] = (address0, self._offset)
return address0
def _read_simple_fragment(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['simple_fragment'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
index1, elements0 = self._offset, []
address1 = FAILURE
address1 = self._read_element()
if address1 is not FAILURE:
elements0.append(address1)
address2 = FAILURE
index2 = self._offset
address2 = self._read_number()
if address2 is FAILURE:
address2 = TreeNode(self._input[index2:index2], index2)
self._offset = index2
if address2 is not FAILURE:
elements0.append(address2)
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
if elements0 is None:
address0 = FAILURE
else:
address0 = self._actions.make_simple_fragment(self._input, index1, self._offset, elements0)
self._offset = self._offset
self._cache['simple_fragment'][index0] = (address0, self._offset)
return address0
def _read_adduct(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['adduct'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
index1, elements0 = self._offset, []
address1 = FAILURE
address1 = self._read_sign()
if address1 is not FAILURE:
elements0.append(address1)
address2 = FAILURE
address2 = self._read_complex()
if address2 is not FAILURE:
elements0.append(address2)
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
if elements0 is None:
address0 = FAILURE
else:
address0 = self._actions.multiply_by_sign(self._input, index1, self._offset, elements0)
self._offset = self._offset
self._cache['adduct'][index0] = (address0, self._offset)
return address0
def _read_sign(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['sign'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
chunk0 = None
if self._offset < self._input_size:
chunk0 = self._input[self._offset:self._offset + 1]
if chunk0 is not None and Grammar.REGEX_1.search(chunk0):
address0 = TreeNode(self._input[self._offset:self._offset + 1], self._offset)
self._offset = self._offset + 1
else:
address0 = FAILURE
if self._offset > self._failure:
self._failure = self._offset
self._expected = []
if self._offset == self._failure:
self._expected.append('[+ -]')
self._cache['sign'][index0] = (address0, self._offset)
return address0
def _read_element(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['element'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
index1, elements0 = self._offset, []
address1 = FAILURE
chunk0 = None
if self._offset < self._input_size:
chunk0 = self._input[self._offset:self._offset + 1]
if chunk0 is not None and Grammar.REGEX_2.search(chunk0):
address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset)
self._offset = self._offset + 1
else:
address1 = FAILURE
if self._offset > self._failure:
self._failure = self._offset
self._expected = []
if self._offset == self._failure:
self._expected.append('[A-Z]')
if address1 is not FAILURE:
elements0.append(address1)
address2 = FAILURE
remaining0, index2, elements1, address3 = 0, self._offset, [], True
while address3 is not FAILURE:
chunk1 = None
if self._offset < self._input_size:
chunk1 = self._input[self._offset:self._offset + 1]
if chunk1 is not None and Grammar.REGEX_3.search(chunk1):
address3 = TreeNode(self._input[self._offset:self._offset + 1], self._offset)
self._offset = self._offset + 1
else:
address3 = FAILURE
if self._offset > self._failure:
self._failure = self._offset
self._expected = []
if self._offset == self._failure:
self._expected.append('[a-z]')
if address3 is not FAILURE:
elements1.append(address3)
remaining0 -= 1
if remaining0 <= 0:
address2 = TreeNode(self._input[index2:self._offset], index2, elements1)
self._offset = self._offset
else:
address2 = FAILURE
if address2 is not FAILURE:
elements0.append(address2)
else:
elements0 = None
self._offset = index1
else:
elements0 = None
self._offset = index1
if elements0 is None:
address0 = FAILURE
else:
address0 = self._actions.make_element(self._input, index1, self._offset, elements0)
self._offset = self._offset
self._cache['element'][index0] = (address0, self._offset)
return address0
def _read_number(self):
address0, index0 = FAILURE, self._offset
cached = self._cache['number'].get(index0)
if cached:
self._offset = cached[1]
return cached[0]
remaining0, index1, elements0, address1 = 1, self._offset, [], True
while address1 is not FAILURE:
chunk0 = None
if self._offset < self._input_size:
chunk0 = self._input[self._offset:self._offset + 1]
if chunk0 is not None and Grammar.REGEX_4.search(chunk0):
address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset)
self._offset = self._offset + 1
else:
address1 = FAILURE
if self._offset > self._failure:
self._failure = self._offset
self._expected = []
if self._offset == self._failure:
self._expected.append('[0-9]')
if address1 is not FAILURE:
elements0.append(address1)
remaining0 -= 1
if remaining0 <= 0:
address0 = self._actions.make_number(self._input, index1, self._offset, elements0)
self._offset = self._offset
else:
address0 = FAILURE
self._cache['number'][index0] = (address0, self._offset)
return address0
[docs]class Parser(Grammar):
def __init__(self, input, actions, types):
self._input = input
self._input_size = len(input)
self._actions = actions
self._types = types
self._offset = 0
self._cache = defaultdict(dict)
self._failure = 0
self._expected = []
[docs] def parse(self):
tree = self._read_formula()
if tree is not FAILURE and self._offset == self._input_size:
return tree
if not self._expected:
self._failure = self._offset
self._expected.append('<EOF>')
raise ParseError(format_error(self._input, self._failure, self._expected))
[docs]def parse(input, actions=None, types=None):
parser = Parser(input, actions, types)
return parser.parse()