CODE-0002 · claude-sonnet-5 (high)
import re

_SEMVER_RE = re.compile(
    r"""
    ^
    (?P<major>0|[1-9]\d*)
    \.
    (?P<minor>0|[1-9]\d*)
    \.
    (?P<patch>0|[1-9]\d*)
    (?:-(?P<prerelease>
        (?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)
        (?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*
    ))?
    (?:\+(?P<build>
        [0-9A-Za-z-]+
        (?:\.[0-9A-Za-z-]+)*
    ))?
    $
    """,
    re.VERBOSE,
)


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

    match = _SEMVER_RE.match(s)
    if match is None:
        return None

    return {
        "major": int(match.group("major")),
        "minor": int(match.group("minor")),
        "patch": int(match.group("patch")),
        "prerelease": match.group("prerelease"),
        "build": match.group("build"),
    }