Day 19

Feelings

Well. Tried to do this in Rust. Ended up learning Python.

Learnings

Found out about regex-module for Python. It supports much more versatile regexes than I've been using.

Solution (both parts)

import regex


def defs(rules_s):
    for rule in rules_s.split('\n'):
        n, s = rule.split(': ', 1)
        s = regex.sub(r'\s*(\d+)\s*', r'(?&r\1)', s)
        s = regex.sub(r'"(\w+)"', r'\1', s)
        yield "(?P<r{}>{})".format(n, s)


rules_s, messages_s = open('./input').read().split("\n\n", 1)
r = regex.compile(r'(?(DEFINE){})^(?&r0)$'.format(''.join(defs(rules_s))))
print("part1:", sum(1 if r.match(msg) else 0 for msg in messages_s.split('\n')))

rules_s, messages_s = open('./input').read().split("\n\n", 1)
rules_s = regex.sub(r'8: 42', '8: 42 | 42 8', rules_s)
rules_s = regex.sub(r'11: 42 31', '11: 42 31 | 42 11 31', rules_s)
# .. or replace with what you had in the instructions page
r = regex.compile(r'(?(DEFINE){})^(?&r0)$'.format(''.join(defs(rules_s))))
print("part2:", sum(1 if r.match(msg) else 0 for msg in messages_s.split('\n')))

This whole solution bases on using regex group definitions. What are they?

Regex group definitions

>>> regex.search(
    r'(?(DEFINE)(?P<quant>\d+)(?P<item>\w+))(?&quant) (?&item)',
    '5 elephants')
<regex.Match object; span=(0, 11), match='5 elephants'>

And they can be recursive so even this works:

>>> regex.search(
    r'(?(DEFINE)(?P<quant>\d+)(?P<ele>ele(?&ele)?)(?P<item>(?&ele)\w+))(?&quant) (?&item)',
    '5 eleeleelephants')
<regex.Match object; span=(0, 17), match='5 eleeleelephants'>

This allow the conversion from rules to regex with group definitions. Every rule is a single definition and references other rules.