unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Yet another command line DSL
@ 2017-12-28 21:50 Amirouche Boubekki
  2017-12-30  0:34 ` sirgazil
  0 siblings, 1 reply; 2+ messages in thread
From: Amirouche Boubekki @ 2017-12-28 21:50 UTC (permalink / raw
  To: Guile User

[-- Attachment #1: Type: text/plain, Size: 2220 bytes --]

Héllo all,


I had to scratch an itch about command line interfaces again.

In Python, I use http://docopt.org/ but it's pain (even if less
painful that argparse and click) because you have to manually
dispatch to the correct function based on a single dictionary
to the correct function.

Long story short, I made a replacement for that, that should improve
the developper experience in guile.

Here is an ascii video of the thing:

    https://asciinema.org/a/VpSS5YTfEuKgAg0J6zwR9ZBxW

It's guix related so you might be able to relate to it.

At the end of the video the code and attached to this mail.

Basically, the idea is to create a nested alist ie. a tree,
where leafs are procedures as specification for the cli.

It looks like that:

(define xote
   `((package
      (install ,package-install)
      (search ,package-search))
     (system
      (init ,system-init)
      (reconfigure ,system-reconfigure)
      (generation
       (switch ,system-generation-switch)
       (list ,system-generation-list)))))

I find it very pleasant to read.

When the user type a command like:

   xote package install guile@2.2.3

The the program will walk the spec and the program arguments
at the same time to look for a possible match. When it finds
a match it checks whether the user wants help otherwise it
executes the command with the rest of the program arguments
that are not matched. In the above example ``'("guile@2.2.3")''
is passed as an arugment of the ``package-install'' procedure.

What I plan to do next, is to provide a generic function that
will look for --optional=arguments in the rest and outputs
two lists:

- an alist of --optional=arguments or short options like -K

- again, the rest of the arguments that were not parsed as
   short or long options.

This simple and generic implementation of arguments will require
the developer using this tool to validate the options if he really
wants. And will allow him to use his favorite cli tool for short
options...

The end result is that we have the best of both world, pleasant
interface that organize its subcommands by topic and the liberty
to use a powerful other framework to parse optional flags.

I find this approach clean and sleak.


WDYT?

[-- Attachment #2: izicli.scm --]
[-- Type: text/plain, Size: 3229 bytes --]

(define-module (izicli))

(use-modules (srfi srfi-1))


;;; izicli

(define (path-display path)
  (let loop ((path path))
    (unless (null? path)
      (display (car path))
      (display " ")
      (loop (cdr path))))
  (display #\newline))

(define (%spec-help path)
  (lambda (spec)
    (if (procedure? (cadr spec))
        (path-display (append path (list (car spec))))
        (for-each (%spec-help (append path (list (car spec)))) (cdr spec)))))

(define (spec-help name spec)
  "Display the usage message for this SPEC for the program named NAME"
  (format #t "Usage:\n\n")
  (for-each (%spec-help (list name)) spec))

(define (help? string)
  (or (string=? string "--help")
      (string=? string "-h")))

(define (%lookup spec args)
  (if (string=? (symbol->string (car spec)) (car args))
      (values (cdr spec) (cdr args))
      (values #f #f)))

(define (lookup spec args)
  (let loop ((spec spec))
    (if (null? spec)
        (values #f #f)
        (call-with-values (lambda () (%lookup (car spec) args))
          (lambda (procedure-or-spec args)
            (cond
             ((and (not procedure-or-spec) (not args))
              (loop (cdr spec)))
             ((procedure? (car procedure-or-spec))
              (values (car procedure-or-spec) args))
             (else (lookup procedure-or-spec args))))))))


(define-public (izicli name spec)
  (let ((args (cdr (program-arguments))))
    (if (or (null? args) (equal? args '("--help")) (equal? args '("-h")))
        (spec-help name spec)
        (call-with-values (lambda () (lookup spec args))
          (lambda (procedure args)
            (cond
             ((and procedure (find help? args))
              (display (procedure-documentation procedure)))
             ((and procedure args)
              (procedure args))
             (else (spec-help name spec))))))))




;;; example

(define (package-install args)
  "Usage:

  xote package install [-ef] PACKAGE

  --expression -e  Interpret PACKAGE as Guile expression and install
                   the result of its evaluation.

  --file -f        Interpret PACKAGE as a filename and install
                   the package by evaluating it.

Install package in your current environment prolly your profile
"
  (pk 'package-install args))

(define (package-search args)
  "Usage:

  xote package search [-rsa] QUERY...

  --regex -r     Interpret query as a regular expression
  --synopsis -s  Consider synopsis during the search too
  --all -a       Consider synopsis and description during the search

Lookup the package index for package that match the given QUERY which
might be multiple words.
"
  (pk 'package-search args))

(define (system-init args)
  (pk 'system-init args))

(define (system-reconfigure arg)
  (pk 'system-reconfigure args))

(define (system-generation-switch args)
  (pk 'system-generation-switch args))

(define (system-generation-list args)
  (pk 'system-generation-list args))


(define xote
  `((package
     (install ,package-install)
     (search ,package-search))
    (system
     (init ,system-init)
     (reconfigure ,system-reconfigure)
     (generation
      (switch ,system-generation-switch)
      (list ,system-generation-list)))))


(izicli 'xote xote)

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: Yet another command line DSL
  2017-12-28 21:50 Yet another command line DSL Amirouche Boubekki
@ 2017-12-30  0:34 ` sirgazil
  0 siblings, 0 replies; 2+ messages in thread
From: sirgazil @ 2017-12-30  0:34 UTC (permalink / raw
  To: Amirouche Boubekki, Guile User

On 28/12/17 16:50, Amirouche Boubekki wrote:
> Héllo all,
> 
> 
> I had to scratch an itch about command line interfaces again.
> 
> In Python, I use http://docopt.org/ but it's pain (even if less
> painful that argparse and click) because you have to manually
> dispatch to the correct function based on a single dictionary
> to the correct function.
> 
> Long story short, I made a replacement for that, that should improve
> the developper experience in guile.
> 
> Here is an ascii video of the thing:
> 
>     https://asciinema.org/a/VpSS5YTfEuKgAg0J6zwR9ZBxW
> 
> It's guix related so you might be able to relate to it.
> 
> At the end of the video the code and attached to this mail.
> 
> Basically, the idea is to create a nested alist ie. a tree,
> where leafs are procedures as specification for the cli.
> 
> It looks like that:
> 
> (define xote
>    `((package
>       (install ,package-install)
>       (search ,package-search))
>      (system
>       (init ,system-init)
>       (reconfigure ,system-reconfigure)
>       (generation
>        (switch ,system-generation-switch)
>        (list ,system-generation-list)))))
> 
> I find it very pleasant to read.
> 
> When the user type a command like:
> 
>    xote package install guile@2.2.3
> 
> The the program will walk the spec and the program arguments
> at the same time to look for a possible match. When it finds
> a match it checks whether the user wants help otherwise it
> executes the command with the rest of the program arguments
> that are not matched. In the above example ``'("guile@2.2.3")''
> is passed as an arugment of the ``package-install'' procedure.
> 
> What I plan to do next, is to provide a generic function that
> will look for --optional=arguments in the rest and outputs
> two lists:
> 
> - an alist of --optional=arguments or short options like -K
> 
> - again, the rest of the arguments that were not parsed as
>    short or long options.
> 
> This simple and generic implementation of arguments will require
> the developer using this tool to validate the options if he really
> wants. And will allow him to use his favorite cli tool for short
> options...
> 
> The end result is that we have the best of both world, pleasant
> interface that organize its subcommands by topic and the liberty
> to use a powerful other framework to parse optional flags.
> 
> I find this approach clean and sleak.
> 
> 
> WDYT?

Thanks, Amirouche! I actually need this :) Nice :)




^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2017-12-30  0:34 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-12-28 21:50 Yet another command line DSL Amirouche Boubekki
2017-12-30  0:34 ` sirgazil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).