(molecular_formula)
| 50 | |
| 51 | |
| 52 | def parse_molecule(molecular_formula): |
| 53 | valid = re.match('([A-Za-z]\d*)+([\+\-]\d*)*$', molecular_formula) |
| 54 | if valid is None: |
| 55 | raise ValueError("Molecular formula \"%s\" is not valid." % |
| 56 | molecular_formula) |
| 57 | |
| 58 | stack = [defaultdict(int)] |
| 59 | |
| 60 | def _parse_formula(formula, _stack): |
| 61 | |
| 62 | # Set remainder equal to 'None' |
| 63 | r = None |
| 64 | |
| 65 | # Regular expression matching for each of the three cases: |
| 66 | atom = re.match(r'([A-Z][a-z]?)(\d+)?', formula) |
| 67 | opening = re.match(r'[\(\[\{]', formula) |
| 68 | closing = re.match(r'[\)\]\}](\d+)?', formula) |
| 69 | |
| 70 | # If atom is identified: |
| 71 | if atom: |
| 72 | r = formula[len(atom.group()):] |
| 73 | _stack[-1][atom.group(1)] += int(atom.group(2) or 1) |
| 74 | |
| 75 | # If opening brackets encountered: |
| 76 | elif opening: |
| 77 | r = formula[len( |
| 78 | opening.group() |
| 79 | ):] # this sets the remainder equal to everything after the opening brackets |
| 80 | _stack.append(defaultdict(int)) |
| 81 | |
| 82 | # If closing brackets encountered: |
| 83 | elif closing: |
| 84 | r = formula[len( |
| 85 | closing.group() |
| 86 | ):] # this sets the remainder equal to everything after the closing brackets |
| 87 | for k, v in _stack.pop().items(): |
| 88 | _stack[-1][k] += v * int( |
| 89 | closing.group(1) |
| 90 | or 1) # v times amount of molecule k, depending on nesting |
| 91 | |
| 92 | # If anything remains, process remainders recursively as nested formulas: |
| 93 | if r: |
| 94 | _parse_formula(r, _stack) |
| 95 | |
| 96 | return dict(_stack[0]) |
| 97 | |
| 98 | result = _parse_formula(molecular_formula, stack) |
| 99 | |
| 100 | charge = re.search('[\+\-]\d*', molecular_formula) |
| 101 | if charge is not None: |
| 102 | charge_str = charge.group() |
| 103 | charge_type = charge_str[0] |
| 104 | if len(charge_str) == 1: |
| 105 | charge_num = 1 |
| 106 | else: |
| 107 | charge_num = int(charge_str[1:]) |
| 108 | result[charge_type] = charge_num |
| 109 |
no test coverage detected