added experimental chaining, still has issues...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-08-26 16:38:19 +03:00
parent 33e4afce60
commit 191790edeb
5 changed files with 110 additions and 11 deletions

View File

@ -54,6 +54,7 @@ For basics see [README.md](./README.md)
- [`<parser>.error(..)`](#parsererror-1)
- [`<parser>.off(..)`](#parseroff)
- [`<parser>(..)`](#parser-1)
- [`Parser.chain(..)` (EXPERIMENTAL)](#parserchain-experimental)
- [Advanced parser API](#advanced-parser-api)
- [`<parser>.print(..)` / `<parser>.printError(..)`](#parserprint--parserprinterror)
- [`<parser>.handlerDefault(..)`](#parserhandlerdefault)
@ -763,6 +764,31 @@ be ignored, otherwise the whole list is processed as if `<main>` was
its head.
### `Parser.chain(..)` (EXPERIMENTAL)
Chain several parsers for staggering option/command processing.
```
Parser.chain(<spec>, <spec>, ..)
-> <parser>
```
This creates a chain of parsers, each processing only the arguments it defines
and passes the rest to the next in chain. This is useful for defining arguments
that need to be processed out of order and before anything else.
This is similar to chaining parsers via `.then(..)` but with additional setup:
- all parsers except the last will have:
- `.splitOptions` set to `false`
- `"-help"` set to `undefined` enabling `-help` pass-through
- `"-*"` and `"@*"` set to `undefined` enabling arguments pass-through
- the last parser will have all the options from the other parsers merged into
it for complete docs/`-help`
XXX the resulting `<parsed>` object will only contain data from the last parser,
this may change in the future.
## Advanced parser API

View File

@ -37,7 +37,7 @@ This code is an evolution of that parser.
- `-version` &ndash; print version
- `-quiet` &ndash; suppress printing
- `-` &ndash; stop argument processing
- Extensible
- Extensible and self-applicable
### Planned

59
argv.js
View File

@ -319,6 +319,44 @@ object.Constructor('Parser', {
toggle: function(v, cur){ return !cur },
},
// XXX this does not merge the parse results... (???)
// XXX EXPERIMENTAL...
chain: function(...parsers){
var Parser = this
var [post, ...pre] = parsers.reverse()
pre.reverse()
// prepare the final parser for merged doc...
// XXX object.deepKeys(..) ???
var final = Parser(Object.assign({},
// set attribute order...
// NOTE: this is here to set the attribute order according
// to priority...
...pre,
// set the correct values...
post,
...pre))
return pre
// setup the chain for arg pass-through...
.map(function(e){
// XXX object.deepKeys(..) ???
return Parser(Object.assign({},
e,
{
splitOptions: false,
'-help': undefined,
'-*': undefined,
'@*': undefined,
})) })
.concat([final])
// chain...
.reduce(function(res, cur){
return res ?
// NOTE: need to call .then(..) on each of the parsers,
// so we return cur to be next...
(res.then(cur), cur)
: cur }, null) },
}, {
// config...
//
@ -1096,8 +1134,10 @@ object.Constructor('Parser', {
return res }
// NOTE: if successful this needs to modify the arg, thus it
// returns both the new first arg and the handler...
// XXX if no handler is found this should return the original
// input arg...
// NOTE: if the first letter is a fail the whole arg will get
// reported...
// XXX do we need to report the specific fail or the whole
// unsplit arg??? (see below)
var splitArgs = function(arg, rest){
var [arg, value] = arg.split(/=/)
// skip single letter unknown or '--' options...
@ -1111,9 +1151,18 @@ object.Constructor('Parser', {
// push the value to the last arg...
value !== undefined
&& r.push(r.pop() +'='+ value)
var h = parsed.handler(a)[1]
// XXX do we need to report the specific fail or the whole
// unsplit arg???
// check the rest of the args...
//if(h && r.reduce(function(r, a){
// return r && parsed.handler(a)[1] }, true)){
if(h){
// push new options back to option "stack"...
rest.splice(0, 0, ...r)
return [ a, parsed.handler(a)[1] ] }
return [ a, h ] }
// no handler found -> return undefined
return [ arg, undefined ] }
try{
// parse/interpret the arguments and call handlers...
@ -1139,6 +1188,7 @@ object.Constructor('Parser', {
: parsed.isCommand(arg) ?
['cmd', COMMAND_PREFIX +'*']
: ['unhandled']
// no handler is found...
if(type == 'unhandled'){
unhandled.push(arg)
continue }
@ -1156,9 +1206,6 @@ object.Constructor('Parser', {
// dynamic or error...
|| parsed.handler(dfl)[1]
// no handler found and '-*' or '@*' not defined...
// XXX if nether the whole arg nor it split are found
// we need to push the original to unhandled...
// ...or is setting .splitOptions to false enough???
if(handler == null){
unhandled.push(arg)
continue }

View File

@ -52,8 +52,34 @@ argv.Parser({
'any other options']},
}))
var parser2 =
exports.parser2 =
argv.Parser.chain({
'-a': {
doc: [
'high priority option',
'this will get processed before',
'any other options'],
handler: function(){
console.log('### high priority option') }},
},{
'-b': {
doc: 'medium priority option',
handler: function(){
console.log('### normal priority option') }},
},{
'-c': {
doc: 'normal priority option',
handler: function(){
console.log('### normal priority option') }},
})
// run the parser...
__filename == (require.main || {}).filename
&& parser(process.argv)
&& parser2()
// vim:set ts=4 sw=4 spell :

View File

@ -1,6 +1,6 @@
{
"name": "ig-argv",
"version": "2.14.0",
"version": "2.15.0",
"description": "simple argv parser",
"main": "argv.js",
"scripts": {