123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- (define-module (semver parser)
- #:use-module (srfi srfi-1)
- #:use-module (ice-9 match)
- #:use-module (ice-9 peg)
- #:use-module (ice-9 pretty-print)
- #:use-module (ice-9 rdelim)
- #:use-module (semver structs)
- #:use-module (semver matcher)
- #:export (parse
- parse-string
- ast->semantic-version))
- (define %debug-level 0) ; 1 verbose
- (define (match-identifier tree)
- "Match TREE for a alphanumeric identifier. Return #f when no such
- tree is recognised."
- (match tree
- (('alphanumeric-identifier v)
- v)
- (('numeric-identifier v)
- (string->number v))
- (_
- #f)))
- ;; TODO: This is ridiculously silly.
- (define (flatten1 lst)
- "Flatten nested cdr list LST, with
- structure (((elem1)((elem2)((elem3))))), and return it as a list with
- structure ((elem1) (elem2) (elem3))."
- (let loop ((acc '())
- (rest lst))
- (cond
- ((null? rest)
- (reverse acc))
- ((not (list? (car rest)))
- (loop (cons rest acc)
- '()))
- (else
- (loop (cons (car rest) acc)
- (cadr rest))))))
- (define (flatten-separated-list tree)
- (match (reverse tree)
- ;; (()
- ;; '())
- ((elem)
- `(,elem))
- (((rest ...) elem)
- `(,elem ,@(flatten1 rest)))))
- (define (parse- input)
- ;; Semantic Version specification
- (define-peg-string-patterns
- "valid-semver <- version-core (hyphen-hide pre-release)? (plus-hide build)?
- version-core <- major period-hide minor period-hide patch
- major <-- numeric-identifier
- minor <-- numeric-identifier
- patch <-- numeric-identifier
- pre-release <-- dot-separated-pre-release-identifiers
- dot-separated-pre-release-identifiers <- pre-release-identifier (period-hide pre-release-identifier)*
- build <-- dot-separated-build-identifiers
- dot-separated-build-identifiers <- build-identifier (period-hide build-identifier)*
- pre-release-identifier <- numeric-identifier / alphanumeric-identifier
- build-identifier <- alphanumeric-identifier
- alphanumeric-identifier <-- identifier-character+
- numeric-identifier <-- (positive-digit digits) / [0] / positive-digit
- identifier-character <- digit / non-digits
- non-digits <- letters / '-'
- digits <- digit+
- digit <- [0] / positive-digit
- positive-digit <- [1-9]
- letters <- [A-Za-z]+
- hyphen-hide < '-'
- plus-hide < '+'
- period-hide < '.'")
- (let* ((match (match-pattern valid-semver input))
- (end (peg:end match))
- (pt (peg:tree match)))
- (if (eq? (string-length input) end)
- pt
- (if match
- (begin
- (format (current-error-port) "parse error: at offset: ~a\n" end)
- (pretty-print pt (current-error-port))
- #f)
- (begin
- (format (current-error-port) "parse error: no match\n")
- #f)))))
- (define (parse-string input)
- (let* ((pt (parse- input))
- (_ (when (> %debug-level 0) (display "tree:\n") (pretty-print pt))))
- pt))
- (define (parse port)
- (parse-string (read-string port)))
- (define (ast->semantic-version ast)
- "Return a <semantic-version> record from an AST representing a semantic
- version."
- (define* (factory major-identifier minor-identifier patch-identifier
- #:optional
- (pre-release-identifier '())
- (build-identifier '()))
- (make-semantic-version
- (match-identifier major-identifier)
- (match-identifier minor-identifier)
- (match-identifier patch-identifier)
- (if (not (null? pre-release-identifier))
- (map match-identifier
- (flatten-separated-list pre-release-identifier))
- pre-release-identifier)
- (if (not (null? build-identifier))
- (map match-identifier
- (flatten-separated-list build-identifier))
- build-identifier)))
- (match ast
- (((('major major) ('minor minor) ('patch patch))
- ('pre-release . pre-release) ('build . build))
- (factory major minor patch pre-release build))
- (((('major major) ('minor minor) ('patch patch))
- ('pre-release . pre-release))
- (factory major minor patch pre-release))
- (((('major major) ('minor minor) ('patch patch))
- ('build . build))
- (factory major minor patch '() build))
- ((('major major) ('minor minor) ('patch patch))
- (factory major minor patch))
- (_
- #f)))
|