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

_SEMVER_RE = re.compile(
    r"(?P<major>0|[1-9][0-9]*)\."
    r"(?P<minor>0|[1-9][0-9]*)\."
    r"(?P<patch>0|[1-9][0-9]*)"
    r"(?:-(?P<prerelease>(?: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"(?:\+(?P<build>[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

    numbers = []
    for name in ("major", "minor", "patch"):
        text = match.group(name)
        try:
            numbers.append(int(text))
        except ValueError:
            value = 0
            for char in text:
                value = value * 10 + (ord(char) - 48)
            numbers.append(value)

    return {
        "major": numbers[0],
        "minor": numbers[1],
        "patch": numbers[2],
        "prerelease": match.group("prerelease"),
        "build": match.group("build"),
    }