CODE-0002 · deepseek-v4-flash (default)
import re

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

    pattern = r'^(?P<major>0|[1-9]\d*)\.' \
              r'(?P<minor>0|[1-9]\d*)\.' \
              r'(?P<patch>0|[1-9]\d*)' \
              r'(?:-(?P<prerelease>[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?' \
              r'(?:\+(?P<build>[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$'

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

    # Convert parts to int
    try:
        major = int(m.group('major'))
        minor = int(m.group('minor'))
        patch = int(m.group('patch'))
    except ValueError:
        return None

    prerelease = m.group('prerelease')
    build = m.group('build')

    # Validate prerelease identifiers: numeric identifiers must not have leading zeros
    if prerelease is not None:
        identifiers = prerelease.split('.')
        for ident in identifiers:
            # Check if all digits
            if ident.isdecimal():
                # Must not have leading zero unless it's exactly "0"
                if len(ident) > 1 and ident[0] == '0':
                    return None
            # else: alphanumeric or hyphen, no further restrictions

    # Build identifiers have no special numeric restrictions

    return {
        'major': major,
        'minor': minor,
        'patch': patch,
        'prerelease': prerelease,
        'build': build
    }