diff --git a/argv.js b/argv.js index 71f632c..a62632d 100644 --- a/argv.js +++ b/argv.js @@ -45,7 +45,7 @@ module.ERROR = // afterCallback(name) // -> func // -// afterCallback(name, pre-action, post-action) +// afterCallback(name, pre_action, post_action) // -> func // // @@ -53,6 +53,13 @@ module.ERROR = // -> this // -> res // +// pre_action(...args) +// -> false +// -> ... +// +// post_action(...args) +// -> ... +// var afterCallback = function(name, pre, post){ var attr = '__after_'+ name return function(...args){ @@ -179,6 +186,7 @@ var afterCallback = function(name, pre, post){ // NOTE: essentially this parser is a very basic stack language... // XXX can we implement the whole thing directly as a stack language??? // +// XXX should a flag have more than one value??? // XXX can we add more prefixes, like '+' and the like??? // ...add prefix handlers??? // XXX should -help should work for any command? @@ -206,7 +214,8 @@ object.Constructor('Parser', { // NOTE: we only care about differentiating an option from a command // here by design... optionInputPattern: /^--?(.*)$/, - commandInputPattern: /^([a-zA-Z*].*)$/, + //commandInputPattern: /^([.0-9a-zA-Z*].*)$/, + commandInputPattern: /^([^-].*)$/, // instance stuff... @@ -303,10 +312,12 @@ object.Constructor('Parser', { handler: function(key){ // clear arg value... key = key.split(/=/).shift() - // option or command? + // normalize option/command name... key = this.optionInputPattern.test(key) ? - key.replace(this.optionInputPattern, this.optionPrefix+'$1') - : key.replace(this.commandInputPattern, this.commandPrefix+'$1') + key.replace(this.optionInputPattern, this.optionPrefix+'$1') + : !key.startsWith(this.commandPrefix) ? + key.replace(this.commandInputPattern, this.commandPrefix+'$1') + : key var seen = new Set() while(key in this && typeof(this[key]) == typeof('str')){ @@ -680,15 +691,20 @@ object.Constructor('Parser', { handler : (handler.handler || defaultHandler(handler))) - .call(parsed, rest, arg, + // XXX should we pass unhandled explicitly??? + // ...if yes then we do not need to splice it back in below... + .call(parsed, + rest, + arg, ...(value != null ? [value] : [])) // add nested parser result parsed... // XXX should this be done also for .STOP / .ERROR / ... ??? - handler instanceof Parser - && parsed.handlerDefault(handler, rest, arg, res) + if(handler instanceof Parser){ + parsed.unhandled.splice(parsed.unhandled.length, 0, ...res.unhandled) + parsed.handlerDefault(handler, rest, arg, res) } res === module.STOP && parsed.stop(arg, rest) @@ -721,6 +737,10 @@ object.Constructor('Parser', { var unhandled = parsed.unhandled = [] while(rest.length > 0){ var arg = rest.shift() + // non-string stuff in arg list... + if(typeof(arg) != typeof('str')){ + unhandled.push(arg) + continue } // NOTE: opts and commands do not follow the same path here // because options if unidentified need to be split into // single letter options and commands to not... diff --git a/test.js b/test.js index 08704a5..b27f14a 100644 --- a/test.js +++ b/test.js @@ -109,6 +109,105 @@ argv.Parser({ +var lang = +module.lang = +argv.Parser({ + // helpers... + push: function(...items){ + this.unhandled.splice(this.unhandled.length, 0, ...items) + return this }, + exec: function(...items){ + this.rest.splice(0, 0, ...items) + return this }, + + pre_ns: argv.Parser({ + }), + + // XXX do not like the split namespaces.... + ns: { + '[': [ 'blockto', '[:]' ], + '(': [ 'blockto', ')', 'exec' ], + 'quote': [ 'quotenn', '0', '1' ], + }, + + '@*': function(code, value){ + this.unhandled.push(...( + // type-convert... + /^[+-]?[0-9]+$/.test(value) ? + [parseInt(value)] + : /^[+-]?[0-9.]+$/.test(value) ? + [parseFloat(value)] + // call user macros... + : value in this.ns ? + (this.exec(...this.ns[value]), []) + // unhandled... + : [value])) }, + + // XXX hanck... + '@quotenn': function(code){ + var skip = code.shift() + var quote = code.shift() + this.push(...code.splice(skip, quote)) }, + + // XXX this needs blocks to be already made... + // :: ( | name code -- | ) + '@::': function(code){ + this.ns[code.shift()] = code.shift() }, + + // XXX revise... + // groupb ( B | .. B -- | [ .. ]) + // groupb ( A:B | .. A .. B .. B -- | [ .. [ .. ] .. ]) + '@blockto': function(code, do_pack, value){ + value = value || code.shift() + value = value instanceof Array ? + value + : value.split(':') + var [f, t] = value.length == 1 ? + [undefined, ...value] + : value + var pack = [] + var cur = code.shift() + while(code.length > 0 && cur != t){ + cur = cur == f ? + this['@blockto'](code, false, value) + : cur + pack.push(cur) + cur = code.shift() } + do_pack !== false + && code.unshift(pack) + return pack }, + '@exec': function(code){ + var c = this.unhandled.pop() + code.splice(0, 0, ...(c instanceof Array ? c : [c])) }, + + '@exit': '-', + + '@dup': function(){ + this.push(...this.unhandled.slice(-1)) }, + '@dup2': function(){ + this.push(...this.unhandled.slice(-2)) }, + + '@print': function(){ + this.print(this.unhandled.pop()) }, + + '@add': function(){ + var [b, a] = [this.unhandled.pop(), this.unhandled.pop()] + this.unhandled.push(a + b) }, + '@sub': function(){ + var [b, a] = [this.unhandled.pop(), this.unhandled.pop()] + this.unhandled.push(a - b) }, + '@mul': function(){ + var [b, a] = [this.unhandled.pop(), this.unhandled.pop()] + this.unhandled.push(a * b) }, + '@div': function(){ + var [b, a] = [this.unhandled.pop(), this.unhandled.pop()] + this.unhandled.push(a / b) }, + +}) +.then(function(){ + this.print('>>>', this.unhandled) }) + + /* console.log(' ->', p(['test', '--verbose', 'a', 'b', 'c'])) @@ -127,7 +226,8 @@ p(['test', '-h']) typeof(__filename) != 'undefined' && __filename == (require.main || {}).filename - && console.log(p()) + //&& console.log(p()) + && console.log(lang())