-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathoptparse.js
More file actions
executable file
·317 lines (294 loc) · 11.3 KB
/
optparse.js
File metadata and controls
executable file
·317 lines (294 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// Optparse.js 1.0.3 - Option Parser for Javascript
//
// Copyright (c) 2009 Johan Dahlberg
//
// See README.md for license.
//
var optparse = {};
try{ optparse = exports } catch(e) {}; // Try to export the lib for node.js
(function(self) {
var VERSION = '1.0.3';
var LONG_SWITCH_RE = /^--\w/;
var SHORT_SWITCH_RE = /^-\w/;
var NUMBER_RE = /^(0x[A-Fa-f0-9]+)|([0-9]+\.[0-9]+)|(\d+)$/;
var DATE_RE = /^\d{4}-(0[0-9]|1[0,1,2])-([0,1,2][0-9]|3[0,1])$/;
var EMAIL_RE = /^([0-9a-zA-Z]+([_.-]?[0-9a-zA-Z]+)*@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+$/;
var EXT_RULE_RE = /(\-\-[\w_-]+)\s+([\w\[\]_-]+)|(\-\-[\w_-]+)/;
var ARG_OPTIONAL_RE = /\[(.+)\]/;
// The default switch argument filter to use, when argument name doesnt match
// any other names.
var DEFAULT_FILTER = '_DEFAULT';
var PREDEFINED_FILTERS = {};
// The default switch argument filter. Parses the argument as text.
function filter_text(value) {
return value;
}
// Switch argument filter that expects an integer, HEX or a decimal value. An
// exception is throwed if the criteria is not matched.
// Valid input formats are: 0xFFFFFFF, 12345 and 1234.1234
function filter_number(value) {
var m = NUMBER_RE.exec(value);
if(m == null) throw OptError('Expected a number representative');
if(m[1]) {
// The number is in HEX format. Convert into a number, then return it
return parseInt(m[1], 16);
} else {
// The number is in regular- or decimal form. Just run in through
// the float caster.
return parseFloat(m[2] || m[3]);
}
};
// Switch argument filter that expects a Date expression. The date string MUST be
// formated as: "yyyy-mm-dd" An exception is throwed if the criteria is not
// matched. An DATE object is returned on success.
function filter_date(value) {
var m = DATE_RE.exec(value);
if(m == null) throw OptError('Expected a date representation in the "yyyy-mm-dd" format.');
return new Date(parseInt(m[0]), parseInt(m[1]) - 1, parseInt(m[2]));
};
// Switch argument filter that expects an email address. An exception is throwed
// if the criteria doesn`t match.
function filter_email(value) {
var m = EMAIL_RE.exec(value);
if(m == null) throw OptError('Excpeted an email address.');
return m[1];
}
// Register all predefined filters. This dict is used by each OptionParser
// instance, when parsing arguments. Custom filters can be added to the parser
// instance by calling the "add_filter" -method.
PREDEFINED_FILTERS[DEFAULT_FILTER] = filter_text;
PREDEFINED_FILTERS['TEXT'] = filter_text;
PREDEFINED_FILTERS['NUMBER'] = filter_number;
PREDEFINED_FILTERS['DATE'] = filter_date;
PREDEFINED_FILTERS['EMAIL'] = filter_email;
// Buildes rules from a switches collection. The switches collection is defined
// when constructing a new OptionParser object.
function build_rules(filters, arr) {
var rules = [];
for(var i=0; i<arr.length; i++) {
var r = arr[i], rule;
if(!contains_expr(r)) throw OptError('Rule MUST contain an option.');
switch(r.length) {
case 1:
rule = build_rule(filters, undefined, r[0]);
break;
case 2:
var expr = LONG_SWITCH_RE.test(r[0]) ? 0 : 1;
var alias = expr == 0 ? -1 : 0;
var desc = alias == -1 ? 1 : -1;
rule = build_rule(filters, r[alias], r[expr], r[desc]);
break;
case 3:
rule = build_rule(filters, r[0], r[1], r[2]);
break;
case 4:
rule = build_rule(filters, r[0], r[1], r[2], r[3]);
break;
default:
case 0:
continue;
}
rules.push(rule);
}
return rules;
}
// Builds a rule with specified expression, short style switch and help. This
// function expects a dict with filters to work correctly.
//
// Return format:
// name The name of the switch.
// short The short style switch
// long The long style switch
// decl The declaration expression (the input expression)
// desc The optional help section for the switch
// optional_arg Indicates that switch argument is optional
// filter The filter to use when parsing the arg. An
// callback The callback for when flag is used
// <<undefined>> value means that the switch does
// not take anargument.
function build_rule(filters, short, expr, desc, callback) {
var optional, filter;
var m = expr.match(EXT_RULE_RE);
if(m == null) throw OptError('The switch is not well-formed.');
var long = m[1] || m[3];
if(m[2] != undefined) {
// A switch argument is expected. Check if the argument is optional,
// then find a filter that suites.
var optional = ARG_OPTIONAL_RE.test(m[2]);
var optional_match = m[2].match(ARG_OPTIONAL_RE);
var filter_name = optional ? optional_match[1] : m[2];
filter = filters[filter_name];
if(filter === undefined) filter = filters[DEFAULT_FILTER];
}
return {
name: long.substr(2),
short: short,
long: long,
decl: expr,
desc: desc,
optional_arg: optional,
filter: filter,
callback: callback
}
}
// Loop's trough all elements of an array and check if there is valid
// options expression within. An valid option is a token that starts
// double dashes. E.G. --my_option
function contains_expr(arr) {
if(!arr || !arr.length) return false;
var l = arr.length;
while(l-- > 0) if(LONG_SWITCH_RE.test(arr[l])) return true;
return false;
}
// Extends destination object with members of source object
function extend(dest, src) {
var result = dest;
for(var n in src) {
result[n] = src[n];
}
return result;
}
// Appends spaces to match specified number of chars
function spaces(arg1, arg2) {
var l, builder = [];
if(arg1.constructor === Number) {
l = arg1;
} else {
if(arg1.length == arg2) return arg1;
l = arg2 - arg1.length;
builder.push(arg1);
}
while(l-- > 0) builder.push(' ');
return builder.join('');
}
// Create a new Parser object that can be used to parse command line arguments.
//
//
function Parser(rules) {
return new OptionParser(rules);
}
// Creates an error object with specified error message.
function OptError(msg) {
return new function() {
this.msg = msg;
this.toString = function() {
return this.msg;
}
}
}
function OptionParser(rules) {
this.banner = 'Usage: [Options]';
this.options_title = 'Available options:'
this._rules = rules;
this._halt = false;
this.filters = extend({}, PREDEFINED_FILTERS);
this.on_args = {};
this.on_switches = {};
this.on_halt = function() {};
this.default_handler = function() {};
}
OptionParser.prototype = {
// Adds args and switchs handler.
on: function(value, fn) {
if(value.constructor === Function ) {
this.default_handler = value;
} else if(value.constructor === Number) {
this.on_args[value] = fn;
} else {
this.on_switches[value] = fn;
}
},
// Adds a custom filter to the parser. It's possible to override the
// default filter by passing the value "_DEFAULT" to the ´´name´´
// argument. The name of the filter is automatically transformed into
// upper case.
filter: function(name, fn) {
this.filters[name.toUpperCase()] = fn;
},
// Parses specified args. Returns remaining arguments.
parse: function(args) {
var result = [], callback;
var rules = build_rules(this.filters, this._rules);
var tokens = args.concat([]);
var token;
while(this._halt == false && (token = tokens.shift())) {
if(LONG_SWITCH_RE.test(token) || SHORT_SWITCH_RE.test(token)) {
var arg = undefined;
// The token is a long or a short switch. Get the corresponding
// rule, filter and handle it. Pass the switch to the default
// handler if no rule matched.
for(var i = 0; i < rules.length; i++) {
var rule = rules[i];
if(rule.long == token || rule.short == token) {
if(rule.filter !== undefined) {
arg = tokens.shift();
if(arg && (!LONG_SWITCH_RE.test(arg) && !SHORT_SWITCH_RE.test(arg))) {
try {
arg = rule.filter(arg, tokens);
} catch(e) {
throw OptError(token + ': ' + e.toString());
}
} else if(rule.optional_arg) {
if(arg) {
tokens.unshift(arg);
}
} else {
throw OptError('Expected switch argument.');
}
}
callback = this.on_switches[rule.name];
if(rule.callback) rule.callback.apply(this, [rule.name, arg])
if (!callback) callback = this.on_switches['*'];
if(callback) callback.apply(this, [rule.name, arg]);
break;
}
}
if(i == rules.length) this.default_handler.apply(this, [token]);
} else {
// Did not match long or short switch. Parse the token as a
// normal argument.
callback = this.on_args[result.length];
result.push(token);
if(callback) callback.apply(this, [token]);
}
}
return this._halt ? this.on_halt.apply(this, [tokens]) : result;
},
// Returns an Array with all defined option rules
options: function() {
return build_rules(this.filters, this._rules);
},
// Add an on_halt callback if argument ´´fn´´ is specified. on_switch handlers can
// call instance.halt to abort the argument parsing. This can be useful when
// displaying help or version information.
halt: function(fn) {
this._halt = fn === undefined
if(fn) this.on_halt = fn;
},
// Returns a string representation of this OptionParser instance.
toString: function() {
var builder = [this.banner, '', this.options_title],
shorts = false, longest = 0, rule;
var rules = build_rules(this.filters, this._rules);
for(var i = 0; i < rules.length; i++) {
rule = rules[i];
// Quick-analyze the options.
if(rule.short) shorts = true;
if(rule.decl.length > longest) longest = rule.decl.length;
}
for(var i = 0; i < rules.length; i++) {
var text = spaces(6);
rule = rules[i];
if(shorts) {
if(rule.short) text = spaces(2) + rule.short + ', ';
}
text += spaces(rule.decl, longest) + spaces(3);
text += rule.desc;
builder.push(text);
}
return builder.join('\n');
}
}
self.VERSION = VERSION;
self.OptionParser = OptionParser;
})(optparse);