Skip to content

Commit

Permalink
OptionParser and related tests needed a cleanup
Browse files Browse the repository at this point in the history
The object returned from OptionParser::parse no longer has a `literals`
property. It was pretty arbitrary, anyway.
  • Loading branch information
michaelficarra committed Jan 26, 2012
1 parent eb5c405 commit c0dac45
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 72 deletions.
6 changes: 3 additions & 3 deletions lib/coffee-script/command.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 25 additions & 23 deletions lib/coffee-script/optparse.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions src/command.coffee
Expand Up @@ -69,9 +69,8 @@ exports.run = ->
return compileStdio() if opts.stdio
return compileScript null, sources[0] if opts.eval
return require './repl' unless sources.length
if opts.run
opts.literals = sources.splice(1).concat opts.literals
process.argv = process.argv[0..1].concat opts.literals
literals = if opts.run then sources.splice 1 else []
process.argv = process.argv[0..1].concat literals
process.argv[0] = 'coffee'
process.execPath = require.main.filename
for source in sources
Expand Down
52 changes: 26 additions & 26 deletions src/optparse.coffee
Expand Up @@ -17,14 +17,13 @@ exports.OptionParser = class OptionParser
@rules = buildRules rules

# Parse the list of arguments, populating an `options` object with all of the
# specified options, and return it. `options.arguments` will be an array
# containing the remaining non-option arguments. `options.literals` will be
# an array of options that are meant to be passed through directly to the
# executing script. This is a simpler API than many option parsers that allow
# you to attach callback actions for every flag. Instead, you're responsible
# for interpreting the options object.
# specified options, and return it. Options after the first non-option
# argument are treated as arguments. `options.arguments` will be an array
# containing the remaining arguments. This is a simpler API than many option
# parsers that allow you to attach callback actions for every flag. Instead,
# you're responsible for interpreting the options object.
parse: (args) ->
options = arguments: [], literals: []
options = arguments: []
skippingArgument = no
originalArgs = args
args = normalizeArguments args
Expand All @@ -34,25 +33,26 @@ exports.OptionParser = class OptionParser
continue
if arg is '--'
pos = originalArgs.indexOf '--'
options.arguments = [originalArgs[1 + pos]]
options.literals = originalArgs[(2 + pos)..]
options.arguments = options.arguments.concat originalArgs[(pos + 1)..]
break
isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
matchedRule = no
for rule in @rules
if rule.shortFlag is arg or rule.longFlag is arg
value = if rule.hasArgument
skippingArgument = yes
args[i + 1]
else
true
options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
matchedRule = yes
break
throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
if not isOption
options.arguments = originalArgs[(originalArgs.indexOf arg)..]
break
# the CS option parser is a little odd; options after the first
# non-option argument are treated as non-option arguments themselves
seenNonOptionArg = options.arguments.length > 0
unless seenNonOptionArg
matchedRule = no
for rule in @rules
if rule.shortFlag is arg or rule.longFlag is arg
value = true
if rule.hasArgument
skippingArgument = yes
value = args[i + 1]
options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
matchedRule = yes
break
throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
if seenNonOptionArg or not isOption
options.arguments.push arg
options

# Return the help text for this **OptionParser**, listing and describing all
Expand All @@ -71,8 +71,8 @@ exports.OptionParser = class OptionParser
# -------

# Regex matchers for option flags.
LONG_FLAG = /^(--\w[\w\-]+)/
SHORT_FLAG = /^(-\w)/
LONG_FLAG = /^(--\w[\w\-]*)/
SHORT_FLAG = /^(-\w)$/
MULTI_FLAG = /^-(\w{2,})/
OPTIONAL = /\[(\w+(\*?))\]/

Expand Down
45 changes: 28 additions & 17 deletions test/option_parser.coffee
Expand Up @@ -13,20 +13,31 @@ opt = new OptionParser [
['-l', '--list [FILES*]', 'desc list']
]

result = opt.parse ['one', 'two', 'three', '-r', 'dir']

eq 5, result.arguments.length
eq '-r', result.arguments[3]

result = opt.parse ['--optional', '-r', 'folder', 'one', 'two']

ok result.optional
eq 'folder', result.required
eq 'one two', result.arguments.join ' '

result = opt.parse ['-l', 'one.txt', '-l', 'two.txt', 'three']

ok result.list instanceof Array
ok result.list.join(' ') is 'one.txt two.txt'
ok result.arguments.join(' ') is 'three'

test "basic arguments", ->
args = ['one', 'two', 'three', '-r', 'dir']
result = opt.parse args
arrayEq args, result.arguments
eq undefined, result.required

test "boolean and parameterised options", ->
result = opt.parse ['--optional', '-r', 'folder', 'one', 'two']
ok result.optional
eq 'folder', result.required
arrayEq ['one', 'two'], result.arguments

test "list options", ->
result = opt.parse ['-l', 'one.txt', '-l', 'two.txt', 'three']
arrayEq ['one.txt', 'two.txt'], result.list
arrayEq ['three'], result.arguments

test "-- and interesting combinations", ->
result = opt.parse ['-o','-r','a','-r','b','-o','--','-a','b','--c','d']
arrayEq ['-a', 'b', '--c', 'd'], result.arguments
ok result.optional
eq 'b', result.required

args = ['--','-o','a','-r','c','-o','--','-a','arg0','-b','arg1']
result = opt.parse args
eq undefined, result.optional
eq undefined, result.required
arrayEq args[1..], result.arguments

0 comments on commit c0dac45

Please sign in to comment.