CODE-0002 · claude-sonnet-4-6 (high)import re
def parse_semver(s):
if not isinstance(s, str):
return None
# Non-negative integer with no leading zeros
num = r'(?:0|[1-9][0-9]*)'
# Prerelease identifier:
# - Contains at least one non-digit [A-Za-z-] character (alphanumeric id), OR
# - Is purely numeric without leading zeros (0 or [1-9][0-9]*)
pre_id = r'(?:[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*|0|[1-9][0-9]*)'
# Build metadata identifier: any non-empty [0-9A-Za-z-]+ (leading zeros allowed)
build_id = r'[0-9A-Za-z-]+'
pattern = (
r'^'
r'(' + num + r')\.'
r'(' + num + r')\.'
r'(' + num + r')'
r'(?:-(' + pre_id + r'(?:\.' + pre_id + r')*))?'
r'(?:\+(' + build_id + r'(?:\.' + build_id + r')*))?'
r'$'
)
m = re.match(pattern, s)
if not m:
return None
major, minor, patch, prerelease, build = m.groups()
return {
'major': int(major),
'minor': int(minor),
'patch': int(patch),
'prerelease': prerelease,
'build': build,
}