Macro
The purpose of the first macro is to make it easy to parse command line arguments in a hierarchical way using the built-in command-line form. The second macro is an additional helper that displays the help message automatically when no command-line argument is specified at this level, which avoids the case where the user tries one argument is then has no information about what to do next.
;; Remove the first argument of the command line arguments (define-syntax-parse-rule (shift-command-line-arguments body ...) (λ args (parameterize ([current-command-line-arguments (vector-copy (current-command-line-arguments) 1)]) body ...))) ;; If the command line arguments are empty, re-parameterize it to ;; default to #("--help") (define-syntax-parse-rule (parameterize-help-if-empty-ccla body ...) (let ([ccla (current-command-line-arguments)]) (parameterize ([current-command-line-arguments (if (vector-empty? ccla) #("--help") ccla)]) body ...)))
Example
#lang racket (require racket/cmdline syntax/parse/define) ;==================; ;=== The macros ===; ;==================; ;; Remove the first argument of the command line arguments (define-syntax-parse-rule (shift-command-line-arguments body ...) (λ args (parameterize ([current-command-line-arguments (vector-copy (current-command-line-arguments) 1)]) body ...))) ;; If the command line arguments are empty, re-parameterize it to ;; default to #("--help") (define-syntax-parse-rule (parameterize-help-if-empty-ccla body ...) (let ([ccla (current-command-line-arguments)]) (parameterize ([current-command-line-arguments (if (vector-empty? ccla) #("--help") ccla)]) body ...))) ;===============; ;=== Example ===; ;===============; (define prog "my-prog") (define (parse-relative) (parameterize-help-if-empty-ccla (command-line #:program (string-append prog " --relative") #:once-each [("--left") => (shift-command-line-arguments (displayln "You're going left!") (parse-main)) '("Go to the left")] [("--right") => (shift-command-line-arguments (displayln "You're going right!") (parse-main)) '("Go to the right")]))) (define (parse-absolute) (parameterize-help-if-empty-ccla (command-line #:program (string-append prog " --absolute") #:once-each [("--north") => (shift-command-line-arguments (displayln "You're going north!") (parse-main)) '("Go to the north")] [("--south") => (shift-command-line-arguments (displayln "You're going south!") (parse-main)) '("Go to the south")]))) (define (parse-move) (parameterize-help-if-empty-ccla (command-line #:program (string-append prog " --move") #:once-each [("--relative") => (shift-command-line-arguments (parse-relative)) '("Specify a relative direction")] [("--absolute") => (shift-command-line-arguments (parse-absolute)) '("Specify an absolute direction")]))) (define (parse-main) (command-line #:program prog #:once-each [("--move") => (shift-command-line-arguments (parse-move)) '("Specify directions")] [("--jump") => (shift-command-line-arguments (displayln "You're jumping!") (parse-main)) '("jump")])) (module+ main (parse-main)) #| Interaction example: $ racket syntax-bee.rkt --move --relative --left --jump --jump --move --absolute --south --jump You're going left! You're jumping! You're jumping! You're going south! You're jumping! |#
Before and After
I've heard several times that command-line can't parse arguments hierarchically. Well, turns out it's easy with syntax-parse! (although admittedly, syntax-rules would have worked too. But at least with syntax-parse it's easy to extend the macro to parse additional keywords, such as if one wants to shift the arguments by more than 1.)
These macros were written for a PR to resyntax. See the diff there.
Licence
Please confirm that you are submitting this code under the same MIT License that the Racket language uses. https://github.com/racket/racket/blob/master/racket/src/LICENSE-MIT.txt
Yes.
Please confirm that the associated text is licensed under the Creative Commons Attribution 4.0 International License http://creativecommons.org/licenses/by/4.0/
Yes.