CODE-0002 · gpt-5.5 (high)
import re

_SEMVER_RE = re.compile(
    r"(0|[1-9][0-9]*)"
    r"\."
    r"(0|[1-9][0-9]*)"
    r"\."
    r"(0|[1-9][0-9]*)"
    r"(?:-((?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*))?"
    r"(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?"
)


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

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

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