diff --git a/argparser.js b/argparser.js index 975636c..829b1b4 100644 --- a/argparser.js +++ b/argparser.js @@ -1,12 +1,119 @@ module.exports = parse; -// Inspired a bit by -// https://github.com/eveningkid/args-parser/blob/master/parse.js +// https://unix.stackexchange.com/questions/364383/confusion-about-changing-meaning-of-arguments-and-options-is-there-an-official +// https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap12.html#tag_12_01 +// 'options' are --foo, -f +// +// 'option-arguments' are (non-option) things that follow an option, and that the option expects +// eg. 'filename.txt' in './example --input filename.txt' +// +// 'operands' are (non-option) things that follow an option, but aren't option-arguments +// eg 'filename.txt' in './example --verbose filename.txt' + + +/** + * @returns [ { string: boolean } | { string: string[] } ] + **/ function parse(argv) { argv = process.argv.slice(2); - let finalOutput = {}; + + // Label everything in argv as either + // an 'option' or an 'arg_or_operand' + let mapped = argv.flatMap((a) => { + // A single long option + if (a.startsWith('--')) { + return { arg: a.substring(2), type: 'option' } + } + + // A single short option + if (a.startsWith('-') && (a.length === 2)) { + return { arg: a.substring(1), type: 'option' } + } + + // Multiple short options + if (a.startsWith('-')) { + let splitGroup = a.substring(1).split(''); + return splitGroup.map((sg) => { return {arg: sg, type: 'option'} }); + } + + // An option-argument or operand + return { arg: a, type: 'arg_or_operand' }; + }); + + // Group consecutive option-args/operands + let wrapped = mapped.map((a) => [a]); + let grouped = []; + wrapped.forEach((a, i) => { + if (i === 0 || a[0].type === 'option') { + grouped.push(a); + return; + } + let prev = grouped.pop(); + if (prev[0].type === 'option') { + grouped.push(prev); + grouped.push(a); + return; + } + grouped.push(prev.concat(a)); + return; + }); + + let out = [] + grouped.forEach((a, i) => { + console.log('current', a); + let next = grouped[i+1]; + console.log('next', next); + + if ((a[0].type === 'option') + && next + && (next[0].type === 'arg_or_operand')) { + console.log('next is a value'); + let next_shortened = next.map(n => n.arg); + console.log('short', next_shortened); + let o = {}; + o[a[0].arg] = next_shortened; + out.push(o); + grouped.splice(i, 1); + } else { + let pair = {}; + pair[a[0].arg] = true; + a.forEach((b) => { + let o = {}; + o[b.arg] = true; + out.push(o); + }); + } + }); + console.log('out', out); +} + + /* + // this is good, but doesn't handle multiple operands in a row + + let out = []; + + mapped.forEach((a, i) => { + console.log(a, i); + if ((a.type === 'flag') && (mapped[i+1].type === 'arg_or_operand')) { + console.log('next is a value'); + let pair = {}; + pair[a.arg] = mapped[i+1].arg; + out.push(pair); + mapped.splice(i, 1); + } else { + let pair = {}; + pair[a.arg] = true; + out.push(pair); + } + }); + //console.log(out); + +} +*/ + + /* argv.forEach((arg) => { // We'll treat `--long` flags and flags without dash prefixes the same, // so just discard double-dash prefixes @@ -61,4 +168,5 @@ function processArg(a) { } else { return [key, true]; } -} \ No newline at end of file +} +*/ \ No newline at end of file