Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hierarchical parsing of command line arguments with the built-in command-line #16

Open
Metaxal opened this issue Aug 7, 2021 · 2 comments

Comments

@Metaxal
Copy link

Metaxal commented Aug 7, 2021

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.

@Metaxal
Copy link
Author

Metaxal commented Aug 10, 2021

Just realized this doesn't fit the purpose of Syntax Parse Bee, sorry! I may try to enhance it to make it a worthwhile example... if I get the time (and an idea).

Feel free to discard it.

@bennn
Copy link
Member

bennn commented Aug 13, 2021

Hm? This looks like a perfect fit to me --- two useful syntax-parse macros. It's fine that these would work as normal syntax rules.

bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 28, 2021
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Nov 1, 2021
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Nov 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants