File: lib/optparse.js

Recommend this page to a friend!
  Classes of Till Wehowski   Widget CLI   lib/optparse.js   Download  
File: lib/optparse.js
Role: Class source
Content type: text/plain
Description: Class source
Class: Widget CLI
Process commands entered by the user
Author: By
Last change:
Date: 7 years ago
Size: 13,377 bytes
 

Contents

Class file image Download
/*!/ Optparse.js 1.0.3 - Option Parser for Javascript // // Copyright (c) 2009 Johan Dahlberg // // See README.md for license. // *edited by webfan.de, Till Wehowski //var optparse = {}; //try{ optparse = exports } catch(e) {}; // Try to export the lib for node.js */ //(function(self) { (function() { 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; 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 // <<undefined>> value means that the switch does // not take anargument. function build_rule(filters, short, expr, desc) { 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 } } // 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_tokens = {}; 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; } return this; }, option: function(value, fn) { return this.on(value, fn); }, /** fn(positionInCL, positionInArgs,arg_token, current_option||*) */ token: function(option, arg, fn) { if('undefined' === typeof this.on_tokens[option]){ this.on_tokens[option] = {}; } this.on_tokens[option][arg] = fn; return this; }, // 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; return this; }, // Parses specified args. Returns remaining arguments. parse: function(args) { var result = [], callback, callback2; var rules = build_rules(this.filters, this._rules); var tokens = args.concat([]); var token, c =0, current_option = /* (args[1] || args[0]) ||*/ '*'; while(this._halt == false && (token = tokens.shift())) { c++; //var p = token.split('='); var p = frdl.explode('=', token, 2); token=p[0]; // var arg = p[1] || true; var arg = false; if(1===p.length && SHORT_SWITCH_RE.test(token)){ arg = true; } else if(2===p.length){ arg = p[1]; } // if('string'===typeof arg && '"' === arg.substr(0,1) && '"' === arg.substr(arg.length-1,arg.length)){ // arg = arg.substr(1,arg.length-1); // } // if('string'===typeof arg && "'" === arg.substr(0,1) && "'" === arg.substr(arg.length-1,arg.length)){ // arg = arg.substr(1,arg.length-1); // } arg = frdl.trim(arg, '"\''); if(LONG_SWITCH_RE.test(token) || SHORT_SWITCH_RE.test(token)) { // 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. var i = 0; for(i = 0; i < rules.length; i++) { var rule = rules[i]; if(rule.long == token || rule.short == token) { if(rule.filter !== undefined) { 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.'); } } current_option=rule.name; callback = this.on_switches[rule.name]; 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 { // if('*'===current_option)current_option=token; // Did not match long or short switch. Parse the token as a // normal argument. // callback = this.on_args[result.length]; callback = this.on_args[result.length]; try{ callback2 = this.on_tokens[current_option][token]; if(!callback2)callback2 = this.on_tokens['*'][token]; if(!callback2)callback2 = this.on_tokens[current_option]['*']; if(!callback2)callback2 = this.on_tokens['*']['*']; }catch(err){ callback2=false; } /*if(!callback2 && !callback)*/result.push(token); if(callback) callback.apply(this, [token]); if(callback2) callback2.apply(this, [c, result.length, token, current_option]); } } 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; return this; }, // 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(3); 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; exports = module.exports = OptionParser; })(); //})(optparse);