Fix semver comparison with different numbers of prerelease elements

By my reading of the semantic versioning spec, the sense of the comparison
of the number of prerelease version segments has to be reversed when one
of those numbers is zero.

In other words, zero prerelease segments takes precedence over any nonzero
number, but any nonzero number takes precedence over any lower nonzero
number.

Example given in the specification:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta <
  1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

I had two failing test cases in foundation-test; in one case, the blessed
output seemed to be wrong, whereas the other case made this fix necessary.
This commit is contained in:
Philip Chimento 2022-04-07 22:34:51 -07:00
parent e628307c1e
commit 436901adb6
3 changed files with 36 additions and 30 deletions

View file

@ -3646,23 +3646,23 @@ void VersionNumbers__to_text(OUTPUT_STREAM, semantic_version_number V) ;
void VersionNumbers__writer(OUTPUT_STREAM, char *format_string, void *vE) ;
#line 144 "inweb/foundation-module/Chapter 7/Version Numbers.w"
semantic_version_number VersionNumbers__from_text(text_stream *T) ;
#line 215 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 217 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__le(semantic_version_number V1, semantic_version_number V2) ;
#line 251 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 255 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__floor(int N) ;
#line 261 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 265 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__strict_atoi(text_stream *T) ;
#line 277 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 281 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__eq(semantic_version_number V1, semantic_version_number V2) ;
#line 283 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__ne(semantic_version_number V1, semantic_version_number V2) ;
#line 287 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__gt(semantic_version_number V1, semantic_version_number V2) ;
int VersionNumbers__ne(semantic_version_number V1, semantic_version_number V2) ;
#line 291 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__ge(semantic_version_number V1, semantic_version_number V2) ;
int VersionNumbers__gt(semantic_version_number V1, semantic_version_number V2) ;
#line 295 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__ge(semantic_version_number V1, semantic_version_number V2) ;
#line 299 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__lt(semantic_version_number V1, semantic_version_number V2) ;
#line 302 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 306 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__cmp(semantic_version_number V1, semantic_version_number V2) ;
#line 39 "inweb/foundation-module/Chapter 7/Version Number Ranges.w"
void VersionNumberRanges__write_range(OUTPUT_STREAM, semver_range *R) ;
@ -8594,11 +8594,11 @@ int CommandLine__read_pair_p(text_stream *opt, text_stream *opt_val, int N,
; innocuous = TRUE; break;
case VERSION_CLSW: {
PRINT("inweb");
char *svn = "7-alpha.1+1A84";
char *svn = "7-alpha.1+1A85";
if (svn[0]) PRINT(" version %s", svn);
char *vname = "Escape to Danger";
if (vname[0]) PRINT(" '%s'", vname);
char *d = "5 April 2022";
char *d = "7 April 2022";
if (d[0]) PRINT(" (%s)", d);
PRINT("\n");
innocuous = TRUE; break;
@ -14494,7 +14494,7 @@ semantic_version_number VersionNumbers__from_text(text_stream *T) {
return V;
}
#line 215 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 217 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__le(semantic_version_number V1, semantic_version_number V2) {
for (int i=0; i<SEMVER_NUMBER_DEPTH; i++) {
int N1 = VersionNumbers__floor(V1.version_numbers[i]);
@ -14504,7 +14504,9 @@ int VersionNumbers__le(semantic_version_number V1, semantic_version_number V2) {
}
linked_list_item *I1 = (V1.prerelease_segments)?(LinkedLists__first(V1.prerelease_segments)):NULL;
linked_list_item *I2 = (V2.prerelease_segments)?(LinkedLists__first(V2.prerelease_segments)):NULL;
while ((I1) && (I2)) {
if ((I1 == NULL) && (I2)) return FALSE;
if ((I1) && (I2 == NULL)) return TRUE;
do {
text_stream *T1 = (text_stream *) LinkedLists__content(I1);
text_stream *T2 = (text_stream *) LinkedLists__content(I2);
int N1 = VersionNumbers__strict_atoi(T1);
@ -14521,19 +14523,19 @@ int VersionNumbers__le(semantic_version_number V1, semantic_version_number V2) {
}
I1 = LinkedLists__next(I1);
I2 = LinkedLists__next(I2);
}
if ((I1 == NULL) && (I2)) return FALSE;
if ((I1) && (I2 == NULL)) return TRUE;
} while ((I1) && (I2));
if ((I1 == NULL) && (I2)) return TRUE;
if ((I1) && (I2 == NULL)) return FALSE;
return TRUE;
}
#line 251 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 255 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__floor(int N) {
if (N < 0) return 0;
return N;
}
#line 261 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 265 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__strict_atoi(text_stream *T) {
LOOP_THROUGH_TEXT(pos, T)
if (Characters__isdigit(Str__get(pos)) == FALSE)
@ -14543,7 +14545,7 @@ int VersionNumbers__strict_atoi(text_stream *T) {
return Str__atoi(T, 0);
}
#line 277 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 281 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__eq(semantic_version_number V1, semantic_version_number V2) {
if ((VersionNumbers__le(V1, V2)) && (VersionNumbers__le(V2, V1)))
return TRUE;
@ -14566,7 +14568,7 @@ int VersionNumbers__lt(semantic_version_number V1, semantic_version_number V2) {
return (VersionNumbers__ge(V1, V2))?FALSE:TRUE;
}
#line 302 "inweb/foundation-module/Chapter 7/Version Numbers.w"
#line 306 "inweb/foundation-module/Chapter 7/Version Numbers.w"
int VersionNumbers__cmp(semantic_version_number V1, semantic_version_number V2) {
if (VersionNumbers__eq(V1, V2)) return 0;
if (VersionNumbers__gt(V1, V2)) return 1;
@ -30934,7 +30936,7 @@ void Ctags__write(web *W, filename *F) {
WRITE("!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n");
WRITE("!_TAG_PROGRAM_AUTHOR\tGraham Nelson\t/graham.nelson@mod-langs.ox.ac.uk/\n");
WRITE("!_TAG_PROGRAM_NAME\tinweb\t//\n");
WRITE("!_TAG_PROGRAM_VERSION\t7-alpha.1+1A84\t/built 5 April 2022/\n");
WRITE("!_TAG_PROGRAM_VERSION\t7-alpha.1+1A85\t/built 7 April 2022/\n");
}
#line 47 "inweb/Chapter 6/Ctags Support.w"

View file

@ -203,13 +203,15 @@ semantic_version_number VersionNumbers::from_text(text_stream *T) {
ADD_TO_LINKED_LIST(Str::duplicate(prerelease), text_stream, V.prerelease_segments);
Str::clear(prerelease);
@h Precendence.
@h Precedence.
The most important part of the semver standard is the rule on which versions
take precedence over which others, and we follow it exactly. The following
criteria are used in turn: major version; minor version; patch version;
any prerelease elements, which must be compared numerically if consisting
of digits only, and alphabetically otherwise; and finally the number of
prerelease elements. Build metadata is disregarded entirely.
the presence or absence of prerelease elements (a prerelease version has
lower precedence); the prerelease elements themselves, which must be
compared numerically if consisting of digits only, and alphabetically
otherwise; and finally the number of prerelease elements. Build metadata is
disregarded entirely.
=
int VersionNumbers::le(semantic_version_number V1, semantic_version_number V2) {
@ -221,7 +223,9 @@ int VersionNumbers::le(semantic_version_number V1, semantic_version_number V2) {
}
linked_list_item *I1 = (V1.prerelease_segments)?(LinkedLists::first(V1.prerelease_segments)):NULL;
linked_list_item *I2 = (V2.prerelease_segments)?(LinkedLists::first(V2.prerelease_segments)):NULL;
while ((I1) && (I2)) {
if ((I1 == NULL) && (I2)) return FALSE;
if ((I1) && (I2 == NULL)) return TRUE;
do {
text_stream *T1 = (text_stream *) LinkedLists::content(I1);
text_stream *T2 = (text_stream *) LinkedLists::content(I2);
int N1 = VersionNumbers::strict_atoi(T1);
@ -238,9 +242,9 @@ int VersionNumbers::le(semantic_version_number V1, semantic_version_number V2) {
}
I1 = LinkedLists::next(I1);
I2 = LinkedLists::next(I2);
}
if ((I1 == NULL) && (I2)) return FALSE;
if ((I1) && (I2 == NULL)) return TRUE;
} while ((I1) && (I2));
if ((I1 == NULL) && (I2)) return TRUE;
if ((I1) && (I2 == NULL)) return FALSE;
return TRUE;
}

View file

@ -21,7 +21,7 @@
3.1.41 > 3.1.5
3.1.41 < 3.2.5
3.1.41 = 3.1.41+arm64
3.1.41 < 3.1.41-pre.0.1
3.1.41 > 3.1.41-pre.0.1
3.1.41-alpha.72 > 3.1.41-alpha.8
3.1.41-alpha.72a < 3.1.41-alpha.8a
3.1.41-alpha.72 < 3.1.41-beta.72