Description
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.