parser.scm 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. (define-module (semver parser)
  2. #:use-module (srfi srfi-1)
  3. #:use-module (ice-9 match)
  4. #:use-module (ice-9 peg)
  5. #:use-module (ice-9 pretty-print)
  6. #:use-module (ice-9 rdelim)
  7. #:use-module (semver structs)
  8. #:use-module (semver matcher)
  9. #:export (parse
  10. parse-string
  11. ast->semantic-version))
  12. (define %debug-level 0) ; 1 verbose
  13. (define (match-identifier tree)
  14. "Match TREE for a alphanumeric identifier. Return #f when no such
  15. tree is recognised."
  16. (match tree
  17. (('alphanumeric-identifier v)
  18. v)
  19. (('numeric-identifier v)
  20. (string->number v))
  21. (_
  22. #f)))
  23. ;; TODO: This is ridiculously silly.
  24. (define (flatten1 lst)
  25. "Flatten nested cdr list LST, with
  26. structure (((elem1)((elem2)((elem3))))), and return it as a list with
  27. structure ((elem1) (elem2) (elem3))."
  28. (let loop ((acc '())
  29. (rest lst))
  30. (cond
  31. ((null? rest)
  32. (reverse acc))
  33. ((not (list? (car rest)))
  34. (loop (cons rest acc)
  35. '()))
  36. (else
  37. (loop (cons (car rest) acc)
  38. (cadr rest))))))
  39. (define (flatten-separated-list tree)
  40. (match (reverse tree)
  41. ;; (()
  42. ;; '())
  43. ((elem)
  44. `(,elem))
  45. (((rest ...) elem)
  46. `(,elem ,@(flatten1 rest)))))
  47. (define (parse- input)
  48. ;; Semantic Version specification
  49. (define-peg-string-patterns
  50. "valid-semver <- version-core (hyphen-hide pre-release)? (plus-hide build)?
  51. version-core <- major period-hide minor period-hide patch
  52. major <-- numeric-identifier
  53. minor <-- numeric-identifier
  54. patch <-- numeric-identifier
  55. pre-release <-- dot-separated-pre-release-identifiers
  56. dot-separated-pre-release-identifiers <- pre-release-identifier (period-hide pre-release-identifier)*
  57. build <-- dot-separated-build-identifiers
  58. dot-separated-build-identifiers <- build-identifier (period-hide build-identifier)*
  59. pre-release-identifier <- numeric-identifier / alphanumeric-identifier
  60. build-identifier <- alphanumeric-identifier
  61. alphanumeric-identifier <-- identifier-character+
  62. numeric-identifier <-- (positive-digit digits) / [0] / positive-digit
  63. identifier-character <- digit / non-digits
  64. non-digits <- letters / '-'
  65. digits <- digit+
  66. digit <- [0] / positive-digit
  67. positive-digit <- [1-9]
  68. letters <- [A-Za-z]+
  69. hyphen-hide < '-'
  70. plus-hide < '+'
  71. period-hide < '.'")
  72. (let* ((match (match-pattern valid-semver input))
  73. (end (peg:end match))
  74. (pt (peg:tree match)))
  75. (if (eq? (string-length input) end)
  76. pt
  77. (if match
  78. (begin
  79. (format (current-error-port) "parse error: at offset: ~a\n" end)
  80. (pretty-print pt (current-error-port))
  81. #f)
  82. (begin
  83. (format (current-error-port) "parse error: no match\n")
  84. #f)))))
  85. (define (parse-string input)
  86. (let* ((pt (parse- input))
  87. (_ (when (> %debug-level 0) (display "tree:\n") (pretty-print pt))))
  88. pt))
  89. (define (parse port)
  90. (parse-string (read-string port)))
  91. (define (ast->semantic-version ast)
  92. "Return a <semantic-version> record from an AST representing a semantic
  93. version."
  94. (define* (factory major-identifier minor-identifier patch-identifier
  95. #:optional
  96. (pre-release-identifier '())
  97. (build-identifier '()))
  98. (make-semantic-version
  99. (match-identifier major-identifier)
  100. (match-identifier minor-identifier)
  101. (match-identifier patch-identifier)
  102. (if (not (null? pre-release-identifier))
  103. (map match-identifier
  104. (flatten-separated-list pre-release-identifier))
  105. pre-release-identifier)
  106. (if (not (null? build-identifier))
  107. (map match-identifier
  108. (flatten-separated-list build-identifier))
  109. build-identifier)))
  110. (match ast
  111. (((('major major) ('minor minor) ('patch patch))
  112. ('pre-release . pre-release) ('build . build))
  113. (factory major minor patch pre-release build))
  114. (((('major major) ('minor minor) ('patch patch))
  115. ('pre-release . pre-release))
  116. (factory major minor patch pre-release))
  117. (((('major major) ('minor minor) ('patch patch))
  118. ('build . build))
  119. (factory major minor patch '() build))
  120. ((('major major) ('minor minor) ('patch patch))
  121. (factory major minor patch))
  122. (_
  123. #f)))