1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00
jashkenas--coffeescript/lib/coffeescript/optparse.js
Danny McClanahan 4e57ca6833 [CS2] Add #! support for executable scripts on Linux. (#3946)
* Add #! support for executable scripts on Linux.

Pass arguments to executable script unchanged if using "#!/usr/bin/env
coffee". (Previously, "./test.coffee -abck" would be turned into "-a -b -c -k",
for example.)

Fixes #1752.

* refactor option parsing

clean up parsing code and in the process fix oustanding bug where coffeescript
modified arguments meant for an executable script

* address comments

* intermediate save

* add note saying where OptionParser is used in coffee command

* add some more work

* fix flatten functions

* refactor tests

* make argument processing less confusing

* add basic test

* remove unused file

* compilation now hangs

* remove unnecessary changes

* add tests!!!

* add/fix some tests

* clarify a test

* fix helpers

* fix opt parsing

* fix infinite loop

* make rule building easier to read

* add tests for flag overlap

* revamp argument parsing again and add more thorough testing

* add tests, comment, clean unused method

* address review comments

* add test for direct invocation of shebang scripts

* move shebang parsing test to separate file and check for browser

* remove TODO

* example backwards compatible warnings

* add correct tests for warning 1

* add tests for warnings

* commit output js libs and update docs

* respond to review comments

also add tests for help text

* respond to review comments

* fix example output

* Rewrite argument parsing documentation to be more concise; add it to sidebar and body; add new output

* Don’t mention deprecated syntax; clean up variable names
2017-07-19 16:25:06 -07:00

181 lines
5.6 KiB
JavaScript

// Generated by CoffeeScript 2.0.0-beta3
(function() {
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat,
slice = [].slice;
({repeat} = require('./helpers'));
exports.OptionParser = OptionParser = class OptionParser {
constructor(ruleDeclarations, banner) {
this.banner = banner;
this.rules = buildRules(ruleDeclarations);
}
parse(args) {
var argument, hasArgument, i, isList, len, name, options, positional, rules;
({rules, positional} = normalizeArguments(args, this.rules.flagDict));
options = {};
for (i = 0, len = rules.length; i < len; i++) {
({hasArgument, argument, isList, name} = rules[i]);
if (hasArgument) {
if (isList) {
if (options[name] == null) {
options[name] = [];
}
options[name].push(argument);
} else {
options[name] = argument;
}
} else {
options[name] = true;
}
}
if (positional[0] === '--') {
options.doubleDashed = true;
positional = positional.slice(1);
}
options.arguments = positional;
return options;
}
help() {
var i, len, letPart, lines, ref, rule, spaces;
lines = [];
if (this.banner) {
lines.unshift(`${this.banner}\n`);
}
ref = this.rules.ruleList;
for (i = 0, len = ref.length; i < len; i++) {
rule = ref[i];
spaces = 15 - rule.longFlag.length;
spaces = spaces > 0 ? repeat(' ', spaces) : '';
letPart = rule.shortFlag ? rule.shortFlag + ', ' : ' ';
lines.push(' ' + letPart + rule.longFlag + spaces + rule.description);
}
return `\n${lines.join('\n')}\n`;
}
};
LONG_FLAG = /^(--\w[\w\-]*)/;
SHORT_FLAG = /^(-\w)$/;
MULTI_FLAG = /^-(\w{2,})/;
OPTIONAL = /\[(\w+(\*?))\]/;
buildRules = function(ruleDeclarations) {
var flag, flagDict, i, j, len, len1, ref, rule, ruleList, tuple;
ruleList = (function() {
var i, len, results;
results = [];
for (i = 0, len = ruleDeclarations.length; i < len; i++) {
tuple = ruleDeclarations[i];
if (tuple.length < 3) {
tuple.unshift(null);
}
results.push(buildRule(...tuple));
}
return results;
})();
flagDict = {};
for (i = 0, len = ruleList.length; i < len; i++) {
rule = ruleList[i];
ref = [rule.shortFlag, rule.longFlag];
for (j = 0, len1 = ref.length; j < len1; j++) {
flag = ref[j];
if (!(flag != null)) {
continue;
}
if (flagDict[flag] != null) {
throw new Error(`flag ${flag} for switch ${rule.name} was already declared for switch ${flagDict[flag].name}`);
}
flagDict[flag] = rule;
}
}
return {ruleList, flagDict};
};
buildRule = function(shortFlag, longFlag, description) {
var match;
match = longFlag.match(OPTIONAL);
shortFlag = shortFlag != null ? shortFlag.match(SHORT_FLAG)[1] : void 0;
longFlag = longFlag.match(LONG_FLAG)[1];
return {
name: longFlag.replace(/^--/, ''),
shortFlag: shortFlag,
longFlag: longFlag,
description: description,
hasArgument: !!(match && match[1]),
isList: !!(match && match[2])
};
};
normalizeArguments = function(args, flagDict) {
var arg, argIndex, flag, i, innerOpts, j, k, lastOpt, len, len1, multiFlags, multiOpts, needsArgOpt, positional, ref, rule, rules, singleRule, withArg;
rules = [];
positional = [];
needsArgOpt = null;
for (argIndex = i = 0, len = args.length; i < len; argIndex = ++i) {
arg = args[argIndex];
if (needsArgOpt != null) {
withArg = Object.assign({}, needsArgOpt.rule, {
argument: arg
});
rules.push(withArg);
needsArgOpt = null;
continue;
}
multiFlags = (ref = arg.match(MULTI_FLAG)) != null ? ref[1].split('').map(function(flagName) {
return `-${flagName}`;
}) : void 0;
if (multiFlags != null) {
multiOpts = multiFlags.map(function(flag) {
var rule;
rule = flagDict[flag];
if (rule == null) {
throw new Error(`unrecognized option ${flag} in multi-flag ${arg}`);
}
return {rule, flag};
});
innerOpts = 2 <= multiOpts.length ? slice.call(multiOpts, 0, j = multiOpts.length - 1) : (j = 0, []), lastOpt = multiOpts[j++];
for (k = 0, len1 = innerOpts.length; k < len1; k++) {
({rule, flag} = innerOpts[k]);
if (rule.hasArgument) {
throw new Error(`cannot use option ${flag} in multi-flag ${arg} except as the last option, because it needs an argument`);
}
rules.push(rule);
}
if (lastOpt.rule.hasArgument) {
needsArgOpt = lastOpt;
} else {
rules.push(lastOpt.rule);
}
} else if ([LONG_FLAG, SHORT_FLAG].some(function(pat) {
return arg.match(pat) != null;
})) {
singleRule = flagDict[arg];
if (singleRule == null) {
throw new Error(`unrecognized option ${arg}`);
}
if (singleRule.hasArgument) {
needsArgOpt = {
rule: singleRule,
flag: arg
};
} else {
rules.push(singleRule);
}
} else {
positional = args.slice(argIndex);
break;
}
}
if (needsArgOpt != null) {
throw new Error(`value required for ${needsArgOpt.flag}, but it was the last argument provided`);
}
return {rules, positional};
};
}).call(this);