srfi37.scm 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. ;;; args-fold.scm - a program argument processor
  2. ;;;
  3. ;;; Copyright (c) 2002 Anthony Carrico
  4. ;;;
  5. ;;; All rights reserved.
  6. ;;;
  7. ;;; Redistribution and use in source and binary forms, with or without
  8. ;;; modification, are permitted provided that the following conditions
  9. ;;; are met:
  10. ;;; 1. Redistributions of source code must retain the above copyright
  11. ;;; notice, this list of conditions and the following disclaimer.
  12. ;;; 2. Redistributions in binary form must reproduce the above copyright
  13. ;;; notice, this list of conditions and the following disclaimer in the
  14. ;;; documentation and/or other materials provided with the distribution.
  15. ;;; 3. The name of the authors may not be used to endorse or promote products
  16. ;;; derived from this software without specific prior written permission.
  17. ;;;
  18. ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  19. ;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20. ;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21. ;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. ;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23. ;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24. ;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25. ;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. ;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. ;;; NOTE: This implementation uses the following SRFIs:
  29. ;;; "SRFI 9: Defining Record Types"
  30. ;;; "SRFI 11: Syntax for receiving multiple values"
  31. ;;;
  32. ;;; NOTE: The scsh-utils and Chicken implementations use regular
  33. ;;; expressions. These might be easier to read and understand.
  34. (module-export
  35. option
  36. option-names
  37. option-required-arg?
  38. option-optional-arg?
  39. option-processor
  40. option?
  41. args-fold)
  42. (define-record-type option-type
  43. (option names required-arg? optional-arg? processor)
  44. option?
  45. (names option-names)
  46. (required-arg? option-required-arg?)
  47. (optional-arg? option-optional-arg?)
  48. (processor option-processor))
  49. (define (args-fold
  50. args
  51. options
  52. unrecognized-option-proc
  53. operand-proc
  54. . seeds)
  55. (letrec
  56. ((find
  57. (lambda (l ?)
  58. (cond ((null? l) #f)
  59. ((? (car l)) (car l))
  60. (else (find (cdr l) ?)))))
  61. (find-option
  62. ;; ISSUE: This is a brute force search. Could use a table.
  63. (lambda (name)
  64. (find
  65. options
  66. (lambda (option)
  67. (find
  68. (option-names option)
  69. (lambda (test-name)
  70. (equal? name test-name)))))))
  71. (scan-short-options
  72. (lambda (index shorts args seeds)
  73. (if (= index (string-length shorts))
  74. (scan-args args seeds)
  75. (let* ((name (string-ref shorts index))
  76. (option (or (find-option name)
  77. (option (list name)
  78. #f
  79. #f
  80. unrecognized-option-proc))))
  81. (cond ((and (< (+ index 1) (string-length shorts))
  82. (or (option-required-arg? option)
  83. (option-optional-arg? option)))
  84. (let-values
  85. ((seeds (apply (option-processor option)
  86. option
  87. name
  88. (substring
  89. shorts
  90. (+ index 1)
  91. (string-length shorts))
  92. seeds)))
  93. (scan-args args seeds)))
  94. ((and (option-required-arg? option)
  95. (pair? args))
  96. (let-values
  97. ((seeds (apply (option-processor option)
  98. option
  99. name
  100. (car args)
  101. seeds)))
  102. (scan-args (cdr args) seeds)))
  103. (else
  104. (let-values
  105. ((seeds (apply (option-processor option)
  106. option
  107. name
  108. #f
  109. seeds)))
  110. (scan-short-options
  111. (+ index 1)
  112. shorts
  113. args
  114. seeds))))))))
  115. (scan-operands
  116. (lambda (operands seeds)
  117. (if (null? operands)
  118. (apply values seeds)
  119. (let-values ((seeds (apply operand-proc
  120. (car operands)
  121. seeds)))
  122. (scan-operands (cdr operands) seeds)))))
  123. (scan-args
  124. (lambda (args seeds)
  125. (if (null? args)
  126. (apply values seeds)
  127. (let ((arg (car args))
  128. (args (cdr args)))
  129. ;; NOTE: This string matching code would be simpler
  130. ;; using a regular expression matcher.
  131. (cond
  132. (;; (rx bos "--" eos)
  133. (string=? "--" arg)
  134. ;; End option scanning:
  135. (scan-operands args seeds))
  136. (;;(rx bos
  137. ;; "--"
  138. ;; (submatch (+ (~ "=")))
  139. ;; "="
  140. ;; (submatch (* any)))
  141. (and (> (string-length arg) 4)
  142. (char=? #\- (string-ref arg 0))
  143. (char=? #\- (string-ref arg 1))
  144. (not (char=? #\= (string-ref arg 2)))
  145. (let loop ((index 3))
  146. (cond ((= index (string-length arg))
  147. #f)
  148. ((char=? #\= (string-ref arg index))
  149. index)
  150. (else
  151. (loop (+ 1 index))))))
  152. ;; Found long option with arg:
  153. => (lambda (=-index)
  154. (let*-values
  155. (((name)
  156. (substring arg 2 =-index))
  157. ((option-arg)
  158. (substring arg
  159. (+ =-index 1)
  160. (string-length arg)))
  161. ((option)
  162. (or (find-option name)
  163. (option (list name)
  164. #t
  165. #f
  166. unrecognized-option-proc)))
  167. (seeds
  168. (apply (option-processor option)
  169. option
  170. name
  171. option-arg
  172. seeds)))
  173. (scan-args args seeds))))
  174. (;;(rx bos "--" (submatch (+ any)))
  175. (and (> (string-length arg) 3)
  176. (char=? #\- (string-ref arg 0))
  177. (char=? #\- (string-ref arg 1)))
  178. ;; Found long option:
  179. (let* ((name (substring arg 2 (string-length arg)))
  180. (option (or (find-option name)
  181. (option
  182. (list name)
  183. #f
  184. #f
  185. unrecognized-option-proc))))
  186. (if (and (option-required-arg? option)
  187. (pair? args))
  188. (let-values
  189. ((seeds (apply (option-processor option)
  190. option
  191. name
  192. (car args)
  193. seeds)))
  194. (scan-args (cdr args) seeds))
  195. (let-values
  196. ((seeds (apply (option-processor option)
  197. option
  198. name
  199. #f
  200. seeds)))
  201. (scan-args args seeds)))))
  202. (;; (rx bos "-" (submatch (+ any)))
  203. (and (> (string-length arg) 1)
  204. (char=? #\- (string-ref arg 0)))
  205. ;; Found short options
  206. (let ((shorts (substring arg 1 (string-length arg))))
  207. (scan-short-options 0 shorts args seeds)))
  208. (else
  209. (let-values ((seeds (apply operand-proc arg seeds)))
  210. (scan-args args seeds)))))))))
  211. (scan-args args seeds)))