For Development HEAD DRAFTSearch (procedure/syntax/module):

9.39 gauche.version - Comparing version numbers

Module: gauche.version

This module provides a convenient procedure to compare version numbers or revision numbers, such as "0.5.1", "3.2-3" or "8.2pl1". Usually each release of software component has a version number, and you can define order between them. For example, version "1.2.3" is newer than "1.2" and older than "2.1". You can compare those version numbers like this:

(version<? "2.2.3" "2.2.11")     ⇒ #t
(version<? "2.3.1" "2.3")        ⇒ #f
(version<? "2.3.1-1" "2.3.1-10") ⇒ #t
(version<? "13a" "5b")           ⇒ #f

There are no standard way to name versions, so I chose one convention. This won’t work for all possible variations, but I think it covers typical cases.

Strictly speaking, you can only define partial order between version numbers, for there can be branches. This module uses simple measure and just assumes the version numbers can be fully ordered.

The version number here is defined by the following syntax.

 <version> : <principal-release>
           | <version> <post-subrelease>
           | <version> <pre-subrelease>
 <principal-release> : <relnum>
 <post-subrelease>   : [.-] <relnum>
 <pre-subrelease>    : _ <relnum>?
 <relnum>            : [^._-]+

Typically <relnum> is composed by numeric part and extension part. For example, "23a" is composed by an integer 23 and extension "a". If <relnum> doesn’t begins with digits, we assume its numeric part is -1.

Then, the order of <relnum> is defined as follows:

  1. If relnum A and relnum B have different numeric part, we ignore the extension and order them numerically, e.g. "3b" < "4a".
  2. If relnum A and relnum B have the same numeric part, we compare extension by alphabetically, e.g. "4c" < "4d" and "5" < "5a".

Given the order of <relnum>, the order of version numbers are defined as follows:

  1. Decompose each version number into a list of <principal-release> and subsequence subrelease components. We call each element of the list "release components".
  2. If the first release component of both lists are the same, remove it from both. Repeat this until the head of the lists differ.
  3. Now we have the following cases.
    1. Both lists are empty: versions are the same.
    2. One list (A) is empty and the other list (B) has post-subrelease at head: A is prior to B
    3. One list (A) is empty and the other list (B) has pre-subrelease at head: B is prior to A
    4. List A’s head is post-subrelease and list B’s head is pre-subrelease: B is prior to A
    5. Both lists have post-subrelease or pre-subrelease at head: compare their relnums.

Here are some examples:

"1" < "1.0" < "1.1" < "1.1.1" < "1.1.2" < "1.2" < "1.11"
"1.2.3" < "1.2.3-1" < "1.2.4"
"1.2.3" < "1.2.3a" < "1.2.3b"
"1.2_" < "1.2_rc0" < "1.2_rc1" < "1.2" < "1.2-pl1" < "1.2-pl2"
"1.1-patch112" < "1.2_alpha"

The reason of having <pre-subrelease> is to allow "release candidate" or "pre-release" version.

A trick: If you want “version 1.2 release or later”, you can say (version<=? "1.2" v). This excludes prerelease versions such as 1.2_pre3. If you want “version 1.2 release or later”, you can say (version<=? "1.2_" v), which includes 1.2_pre1 etc., but excludes anything below, such as 1.1.99999.

It is common if you want to specify acceptable versions with combination of conditions, e.g. “version 1.3 or later, except version 1.4.1” or “greater than version 1.1 and below 1.5”. A version spec is an S-expression to represent that condition. You can use version-satisfy? to check if given version satisfies the spec.

The syntax of version spec is as follows.

<version-spec> : <version>
               | (<op> <version>)
               | (and <version-spec> ...)
               | (or <version-spec> ...)
               | (not <version-spec>)

<version> : version string
<op>      : = | < | <= | > | >=
Function: version=? ver1 ver2
Function: version<? ver1 ver2
Function: version<=? ver1 ver2
Function: version>? ver1 ver2
Function: version>=? ver1 ver2

{gauche.version} Returns a boolean value depending on the order of two version number string ver1 and ver2. If the arguments contain invalid strings as the defined version number, an error is signaled.

Function: version-compare ver1 ver2

{gauche.version} Compares two version number strings ver1 and ver2, and returns either -1, 0, or 1, depending whether ver1 is prior to ver2, ver1 is the same as ver2, or ver1 is after ver2, respectively.

Function: relnum-compare rel1 rel2

{gauche.version} This is lower-level procedure of version-compare. Compares two release numbers (relnums) rel1 and rel2, and returns either -1, 0, or 1 depending whether rel1 is prior to rel2, rel1 is the same as rel2, or rel1 is after rel2, respectively.

The following procedures are to check if a given version satisfies a version specification.

Function: valid-version-spec? spec

{gauche.version} This is a syntax checker. Returns #t if spec is a valid version specification, #f otherwise. See gauche.version module description for the definition of version specification.

Function: version-satisfy? spec version

{gauche.version} Returns #t if version satisfies a version specification spec, #f otherwise. See gauche.version module description for the definition of version specification.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT