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>.error(..)`](#parsererror-1)
- [`<parser>.off(..)`](#parseroff) - [`<parser>.off(..)`](#parseroff)
- [`<parser>(..)`](#parser-1) - [`<parser>(..)`](#parser-1)
- [`Parser.chain(..)` (EXPERIMENTAL)](#parserchain-experimental)
- [Advanced parser API](#advanced-parser-api) - [Advanced parser API](#advanced-parser-api)
- [`<parser>.print(..)` / `<parser>.printError(..)`](#parserprint--parserprinterror) - [`<parser>.print(..)` / `<parser>.printError(..)`](#parserprint--parserprinterror)
- [`<parser>.handlerDefault(..)`](#parserhandlerdefault) - [`<parser>.handlerDefault(..)`](#parserhandlerdefault)
@ -763,6 +764,31 @@ be ignored, otherwise the whole list is processed as if `<main>` was
its head. 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 ## Advanced parser API

View File

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

63
argv.js
View File

@ -319,6 +319,44 @@ object.Constructor('Parser', {
toggle: function(v, cur){ return !cur }, 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... // config...
// //
@ -1096,8 +1134,10 @@ object.Constructor('Parser', {
return res } return res }
// NOTE: if successful this needs to modify the arg, thus it // NOTE: if successful this needs to modify the arg, thus it
// returns both the new first arg and the handler... // returns both the new first arg and the handler...
// XXX if no handler is found this should return the original // NOTE: if the first letter is a fail the whole arg will get
// input arg... // reported...
// XXX do we need to report the specific fail or the whole
// unsplit arg??? (see below)
var splitArgs = function(arg, rest){ var splitArgs = function(arg, rest){
var [arg, value] = arg.split(/=/) var [arg, value] = arg.split(/=/)
// skip single letter unknown or '--' options... // skip single letter unknown or '--' options...
@ -1111,9 +1151,18 @@ object.Constructor('Parser', {
// push the value to the last arg... // push the value to the last arg...
value !== undefined value !== undefined
&& r.push(r.pop() +'='+ value) && r.push(r.pop() +'='+ value)
// push new options back to option "stack"... var h = parsed.handler(a)[1]
rest.splice(0, 0, ...r) // XXX do we need to report the specific fail or the whole
return [ a, parsed.handler(a)[1] ] } // 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, h ] }
// no handler found -> return undefined
return [ arg, undefined ] }
try{ try{
// parse/interpret the arguments and call handlers... // parse/interpret the arguments and call handlers...
@ -1139,6 +1188,7 @@ object.Constructor('Parser', {
: parsed.isCommand(arg) ? : parsed.isCommand(arg) ?
['cmd', COMMAND_PREFIX +'*'] ['cmd', COMMAND_PREFIX +'*']
: ['unhandled'] : ['unhandled']
// no handler is found...
if(type == 'unhandled'){ if(type == 'unhandled'){
unhandled.push(arg) unhandled.push(arg)
continue } continue }
@ -1156,9 +1206,6 @@ object.Constructor('Parser', {
// dynamic or error... // dynamic or error...
|| parsed.handler(dfl)[1] || parsed.handler(dfl)[1]
// no handler found and '-*' or '@*' not defined... // 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){ if(handler == null){
unhandled.push(arg) unhandled.push(arg)
continue } continue }

View File

@ -52,8 +52,34 @@ argv.Parser({
'any other options']}, '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... // run the parser...
__filename == (require.main || {}).filename __filename == (require.main || {}).filename
&& parser(process.argv) && parser2()
// vim:set ts=4 sw=4 spell : // vim:set ts=4 sw=4 spell :

View File

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