| 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 | |