class CTioga2::Commands::Parsers::CommandLineParser

This class is in charge of parsing a command-line against a list of known commands.

Attributes

commands[R]

The list of commands

default_command[R]

A [number of args, Command] for the default command, ie the one that applies on non-command files.

long_options[R]

A hash 'long-option-name' => [number of args, Command]

short_options[R]

A hash 'short-option-letter' => [number of args, Command]

Public Class Methods

new(commands, default = nil) click to toggle source

Creates a CommandLineParser that will understand the given commands

# File lib/ctioga2/commands/parsers/command-line.rb, line 57
def initialize(commands, default = nil)
  @commands = commands
  prepare_option_hashes(default)
end

Public Instance Methods

parse_command_line(argv, interpreter) { |current| ... } click to toggle source

Takes an argv array representing the command-line and a target intepreter, and runs the commands found on the command line. Yields arguments which are not part of a command, or feed them to the default_command if it was specified.

# File lib/ctioga2/commands/parsers/command-line.rb, line 68
def parse_command_line(argv, interpreter)
  # We duplicate the original array
  argv = argv.dup
  options = nil         # currently never used.
  number = 0
  while argv.size > 0
    current = argv.shift
    if current =~ /^--(.*)/ # a long option
      if @long_options.key?($1)
        command, arguments, options = 
          extract_command_arguments(argv, @long_options[$1])

        number += 1
        interpreter.context.parsing_option(current, number)
        interpreter.run_command(command, arguments, options)
      else
        raise OptionUnkown, "Long option #{current} is not known"
      end
    elsif current =~ /^-(.*)/ # Short options
      # We do the same as above, but splitting into letters first:
      short_options = $1.split('')
      for short in short_options
        if @short_options.key?(short)
          command, arguments, options = 
            extract_command_arguments(argv, @short_options[short])
          number += 1
          interpreter.context.parsing_option("-#{short}", number)
          interpreter.run_command(command, arguments, options)
        else
          raise OptionUnkown, "Short option -#{short} is not known"
        end
      end
    else
      if @default_command
        argv.unshift current
        command, arguments, options = 
          extract_command_arguments(argv, @default_command)
        number += 1
        interpreter.context.parsing_option("(default)", number)
        interpreter.run_command(command, arguments, options)
      else
        yield current
      end
    end
  end
end

Protected Instance Methods

extract_command_arguments(argv, cmd_val) click to toggle source

Extract command, arguments and potential options from the given argv array. The second argument is what is stored in the short_options and long_options hashes.

Returns an array

[command, arguments, options]
# File lib/ctioga2/commands/parsers/command-line.rb, line 160
def extract_command_arguments(argv, cmd_val)
  number, command = cmd_val
  options = {}

  # Special case for boolean arguments
  if number < 0
    arguments = [number == -1]
  else
    arguments = argv.slice!(0,number)
  end
  
  # We try and go fishing for options, in the form
  # /option=stuff, or /option stuff...
  while argv.first =~ /^\/([\w-]+)(?:=(.*))?$/
    if command.has_option? $1
      argv.shift
      if $2
        options[$1] = $2
      else
        options[$1] = argv.shift
      end
    else
      warn { "Argument #{argv.first} looks like an option, but does not match any of the command #{command.name}" }
      break
    end
  end

  return [command, arguments, options]

end
prepare_option_hashes(default = nil) click to toggle source

Prepares the short_options and long_options hashes for use in parse_command_line

# File lib/ctioga2/commands/parsers/command-line.rb, line 119
def prepare_option_hashes(default = nil)
  @short_options = {} 
  @long_options = {}
  for cmd in @commands
    short = cmd.short_option
    boolean = (cmd.argument_number == 1 && 
               cmd.arguments.first.type.boolean?)
    if short 
      if @short_options.key? short
        raise OptionRedefined, "Short option #{short} was already defined as command #{cmd.name}"
      end
      if boolean
        @short_options[short] = [-1, cmd]
      else
        @short_options[short] = [cmd.argument_number, cmd]
      end
    end
    long = cmd.long_option
    if long
      if @long_options.key? short
        raise OptionRedefined, "Long option #{long} was already defined as command #{cmd.name}"
      end
      if boolean
        @long_options[long] = [-1, cmd]
        @long_options["no-#{long}"] = [-2, cmd]
      else
        @long_options[long] = [cmd.argument_number, cmd]
      end
    end
  end
  if default
    @default_command = [default.argument_number, default]
  end
end