c-blake / cligen

Nim library to infer/generate command-line-interfaces / option / argument parsing; Docs at
https://c-blake.github.io/cligen/
ISC License
509 stars 24 forks source link

Error: <function name> has no param matching `help` key "0" #120

Closed Nindaleth closed 4 years ago

Nindaleth commented 4 years ago

Nim version: 1.0.0 cligen version: 0.9.38

Affected source:

proc repro(secondsSpread = 300, `0` = false, b = false) =
  echo "0 is: ", $`0`

import cligen

dispatch(repro,
  cmdName = "./repro0.nim",
  help = {
    "secondsSpread": "length of time window covered by one file",
    # commenting out the next line fixes the issue
    "0": "NUL-delimit the file in --outputFile instead of newline-delimiting",
    "b": "single letter parameters don't suffer",
  },
)

Notes:

Expected result:

0 is: false

Obtained result:

stack trace: (most recent call last)
/home/liskar/.nimble/pkgs/cligen-0.9.38/cligen.nim(276, 25) dispatchGen
/home/liskar/.nimble/pkgs/cligen-0.9.38/cligen.nim(133, 13) parseHelps
/home/liskar/src/personal-tools/sorting/repro0.nim(6, 9) template/generic instantiation of `dispatch` from here
/home/liskar/.nimble/pkgs/cligen-0.9.38/cligen.nim(685, 14) template/generic instantiation of `dispatchCf` from here
/home/liskar/.nimble/pkgs/cligen-0.9.38/cligen.nim(671, 14) template/generic instantiation of `dispatchGen` from here
/home/liskar/.nimble/pkgs/cligen-0.9.38/cligen.nim(133, 13) Error: repro has no param matching `help` key "0"
c-blake commented 4 years ago

Hmm...Probably related to using ==(ident, ident) instead of optionNormalize. Should be easy to fix, but might take me a while to figure out the right fix.

In the meanwhile, if you just want a short -0 flag then you could also name the parameter zero, put in a short={'0': "zero"}, and change the help "0" to "zero". That would also remove the need to backquote the param name in the proc definition. (Might be obvious..only mention in case it didn't occur to you.)

Nindaleth commented 4 years ago

Actually, it didn't :-) Initially, I wanted to keep just -0 as an option, but adding a long form --print0 seems logical. Thanks for the pointer! And no need to hurry with the fix, I already had a workaround available (older cligen version).

c-blake commented 4 years ago

Cool. Glad to have helped. If it's for the kind of safe delimiting I suspect, -0, --print0 is kinda standard anyway. (I guess find uses a single dash, but it has a pretty idiosyncratic CLI.)

In two of the programs in examples/, newest & dups, I just take an arbitrary char delimiter defaulting to newline. You have to type -d\\0 or some such to use that. Perhaps too far a departure from the logic you want. Perhaps not.

In general, testing with backquoted idents in cligen is non-existent. Quite a few things besides this might be broken. Backquoted - would almost certainly fail and that might not be easy/even possible to fix.

Nindaleth commented 4 years ago

Yeah, it's exactly for that safe delimiting. I have both the "producer" and the "consumer", thus limiting the options to -0 means less difference in the options between the two. I can continue to use -0 even with the long options and the only challenge left is to invent an appropriate counterpart to --print0... --from0, --read0, --input0...?

-d\\0 gives too much flexibility (for my use) in exchange for slightly less friendly input; I'm content with only allowing \n and \0 depending on a flag.

- as a parameter name is pretty hardcore. As a Linux user, the only possible use I can imagine for - is to have it as a positional parameter for stdin/stdout. But then it wouldn't be an issue in cligen - that already works.

c-blake commented 4 years ago

Yeah...dash as an argument to an option or just a non-option parameter works..Just not as a parameter/option name/key. It probably could never work as such since the shell syntax would be ambiguous between the beginning of a long option and the beginning of a bank of short options. On the other side, it's possible, syntactically, to have option keys that would be illegal Nim identifiers (e.g. ending in underscore). Those get blocked in multiple ways by cligen since it makes the most sense to me to stick to the same style insensitivity rules (expanded to be dash-insensitive, too). So, perfect Nim generality/correspondence can probably never be achieved/wouldn't be strictly "wanted".

The producer-consumer delimiting situation is definitely a common one. I agree newline & zero mostly cover the cases. That's what xargs does, after all. It's probably unnecessarily draconian, but you could even just always do zero and force users to tr \\n \\0 or vice-versa.

Somewhat random aside, but actually storing (as opposed to merely piping) the zero bytes can make a list of strings in a file "mmap"-able and still usable as terminated C strings with no copies. Why, if you cache a separate "offset table" you get an entirely zero copy API to such. Justtr and grep -b '$'|sed 's/:.*//' let you build that pile of terminated strings and offsets. { Well, it might be helpful to parse the offset numbers into binary ints so that file could also be mmap()d or even better just patch tr/write a little program to emit such an offset-of-changes file with the main data. } Of course, that only makes sense in cases you'll be accessing the same data over and over again. For static data, the offsets could be a prefix to the data, while separating them gives you easy append-only updates. Some of the interfaces I provide in cligen/[mfile, mslice] might be useful for such a situation, not that I think it's necessarily your specific situation for this specific program. Just vaguely related.

c-blake commented 4 years ago

(In case "separating them" was unclear, I mean into two filesystem files - a string offset file and a data file vs. some (length-prefixed) array of ints at the beginning followed by the NUL-byte splitable data blob afterward.)

Nindaleth commented 4 years ago

Thanks for the fix!

I'm sorry I didn't respond to the last few paragraphs. I think it got a little over my head, also I fear it's not completely applicable to my usage.

c-blake commented 4 years ago

Oh, it's fine. It was a little off topic/in the weeds. It's mostly about how to "not parse over and over again" even when the "parsing" is as limited/simple as just finding some '\0' byte or other delimiter. Too many people seem to be unaware of how easy it can be to do "memory map & go" interfaces. I use these a lot in https://github.com/c-blake/suggest but that is even more off topic.