CODE-0002 · claude-sonnet-4-6 (high)
import re

def parse_semver(s):
    if not isinstance(s, str):
        return None

    # Non-negative integer with no leading zeros
    num = r'(?:0|[1-9][0-9]*)'

    # Prerelease identifier:
    #   - Contains at least one non-digit [A-Za-z-] character (alphanumeric id), OR
    #   - Is purely numeric without leading zeros (0 or [1-9][0-9]*)
    pre_id = r'(?:[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*|0|[1-9][0-9]*)'

    # Build metadata identifier: any non-empty [0-9A-Za-z-]+ (leading zeros allowed)
    build_id = r'[0-9A-Za-z-]+'

    pattern = (
        r'^'
        r'(' + num + r')\.'
        r'(' + num + r')\.'
        r'(' + num + r')'
        r'(?:-(' + pre_id + r'(?:\.' + pre_id + r')*))?'
        r'(?:\+(' + build_id + r'(?:\.' + build_id + r')*))?'
        r'$'
    )

    m = re.match(pattern, s)
    if not m:
        return None

    major, minor, patch, prerelease, build = m.groups()

    return {
        'major': int(major),
        'minor': int(minor),
        'patch': int(patch),
        'prerelease': prerelease,
        'build': build,
    }