CODE-0002 · gpt-5.4-mini (high)
import re

_SEMVER_RE = re.compile(
    r"(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"
    r"(?:-([0-9A-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

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

    major_s, minor_s, patch_s, prerelease, build = m.groups()

    if prerelease is not None:
        for ident in prerelease.split("."):
            if ident.isdigit() and len(ident) > 1 and ident[0] == "0":
                return None

    return {
        "major": int(major_s),
        "minor": int(minor_s),
        "patch": int(patch_s),
        "prerelease": prerelease,
        "build": build,
    }