Skip to content

Instantly share code, notes, and snippets.

@dunn
Created November 28, 2015 05:30
Show Gist options
  • Save dunn/ae99094ce3524e8d1bc5 to your computer and use it in GitHub Desktop.
Save dunn/ae99094ce3524e8d1bc5 to your computer and use it in GitHub Desktop.
(defun version-to-list (ver)
"Convert version string VER into a list of integers.
The version syntax is given by the following EBNF:
VERSION ::= NUMBER ( SEPARATOR NUMBER )*.
NUMBER ::= (0|1|2|3|4|5|6|7|8|9)+.
SEPARATOR ::= `version-separator' (which see)
| `version-regexp-alist' (which see).
The NUMBER part is optional if SEPARATOR is a match for an element
in `version-regexp-alist'.
Examples of valid version syntax:
1.0pre2 1.0.7.5 22.8beta3 0.9alpha1 6.9.30Beta OTP-10.0.3 2.4.snapshot
Examples of invalid version syntax:
1.0prepre2 1.0..7.5 22.8X3 .5
Examples of version conversion:
Version String Version as a List of Integers
\"1.0.7.5\" (1 0 7 5)
\"0.9snapshot\" (0 9 -4)
\"1.0-git\" (1 0 -4)
\"1.0.cvs\" (1 0 -4)
\"1.0pre2\" (1 0 -1 2)
\"1.0PRE2\" (1 0 -1 2)
\"22.8beta3\" (22 8 -2 3)
\"22.8 Beta3\" (22 8 -2 3)
\"0.9alpha1\" (0 9 -3 1)
\"0.9AlphA1\" (0 9 -3 1)
\"0.9 alpha\" (0 9 -3)
See documentation for `version-separator' and `version-regexp-alist'."
(or (and (stringp ver) (> (length ver) 0))
(error "Invalid version string: `%s'" ver))
;; Change .x.y to 0.x.y
(if (and (>= (length ver) (length version-separator))
(string-equal (substring ver 0 (length version-separator))
version-separator))
(setq ver (concat "0" ver)))
;; Strip any non-numeric prefix (see Erlang and their OTP-18.1.5 version-strings)
(while (not (string-match "^[0-9]" ver))
(setq ver (substring ver 1)))
(save-match-data
(let ((i 0)
(case-fold-search t) ; ignore case in matching
lst s al)
;; Starting from the beginning of the string (i = 0), find
;; number-y chunks and parse them. A "number-y chunk" is a
;; technical term for a string that begins with a number but
;; might have non-numeric elements.
(while (and (setq s (string-match "[0-9]+" ver i))
(= s i))
;; Add the numeric part to the beginning of lst
(setq lst (cons (string-to-number (substring ver i (match-end 0))) lst)
;; set i to the end of the numeric part
i (match-end 0))
(message lst)
;; handle non-numeric part
(when (and (setq s (string-match "[^0-9]+" ver i))
(= s i))
(setq s (substring ver i (match-end 0))
i (match-end 0))
(message s)
;; handle alpha, beta, pre, etc. separator
(unless (string= s version-separator)
(setq al version-regexp-alist)
(while (and al (not (string-match (caar al) s)))
(setq al (cdr al)))
(cond
;; If the separator matches one of the regexps from the
;; alist, add its priority to lst
(al (push (cdar al) lst))
;; If it doesn't match the alist, it might still be
;; salvageable.
;;
;; Convert 22.3a to 22.3.1, 22.3b to 22.3.2, etc.
((string-match "^[-\._+ ]?\\([a-zA-Z]\\)$" s)
(push (- (aref (downcase (match-string 1 s)) 0) ?a -1)
lst))
;; If it can't be parsed:
(t (error "Invalid version syntax: `%s'" ver))))))
(if (null lst)
(error "Invalid version syntax: `%s'" ver)
(nreverse lst)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment