class CTioga2::Commands::Parsers::FileParser

This class parses a “new style” command file, delegating to the old parser if this looks like an old style command file.

Public Class Methods

run_command_file(file, interpreter) click to toggle source

Runs a command file targeting the given interpreter.

# File lib/ctioga2/commands/parsers/file.rb, line 36
def self.run_command_file(file, interpreter)
  FileParser.new.run_command_file(file, interpreter)
end
run_commands(strings, interpreter) click to toggle source

Runs the given command strings

# File lib/ctioga2/commands/parsers/file.rb, line 41
def self.run_commands(strings, interpreter)
  FileParser.new.run_commands(strings, interpreter)
end

Public Instance Methods

parse_io_object(io, interpreter) click to toggle source

Parses a given io object, sending commands/variable definitions to the given interpreter.

# File lib/ctioga2/commands/parsers/file.rb, line 59
def parse_io_object(io, interpreter)

  # We first split everything into lines
  lines = io.readlines
  
  # First, we look for old-style commands to see if we
  # delegate to the other parser. In any case, we build up a
  # list of "unsplit" lines (ie gather lines that end with \)

  parsed_lines = []
  cur = nil 

  lines_indices = []
  idx = 0
  

  has_ruby = false
  ## @todo line counting ?
  for l in lines
    idx += 1
    # If we find something that looks like an old  command at the
    # beginning of a line, we say this is an old style file.

    ## @todo Find a way to disable this compatibility stuff --
    ## or make it more accurate ? The problem is that in a
    ## large command file, there may be things that look like
    ## old style commands ?

    if l =~ /^\s*ruby\s*$/
      has_ruby = true
    elsif l =~ /^([a-z0-9-]+)\(/ && (!has_ruby)
      path = io.respond_to?(:path) ? io.path : io.to_s
      warn { "Found old style (deprecated) commands in '#{path}', using old style parser"}
      return OldFileParser.new.
        run_commands(lines.join(""), interpreter)
    end
    if cur
      cur << l
    else
      cur = l
    end

    if cur =~ /\\$/ 
      cur.gsub!(/\\$/,'')
      cur.chomp!
    else
      # Strip all white space at the end of unfinished lines.
      parsed_lines << cur.gsub(/\s+$/,"\n")
      lines_indices << idx
      cur = nil
    end
    
  end

  # Flush any pending unfinished line
  parsed_lines << cur if cur
  lines_indices << idx if cur

  # Now, we rearrange the lines...
  idx = -1
  ruby = false

  # False, or a [var, values, code] triplet
  loop = false
  for l in parsed_lines
    idx += 1
    interpreter.context.parsing_file(nil, io, lines_indices[idx])
    if l =~ /^\s*ruby\s*$/
      ruby = ""
    elsif ruby
      if l =~ /^\s*ruby\s+end\s*$/
        begin
          Ruby.run_code(ruby)
          ruby = false
        rescue Exception => e
          fatal { "Error #{e.inspect} running inline Ruby code at #{interpreter.context}" }
        end
      else
        ruby << l
      end
    elsif l =~ /^\s*for\s+(\w+)\s+in\s+(.*)/
      v = $2
      var = $1
      v << "\n"
      s = InterpreterString.parse_until_unquoted(StringIO.new(v),"\n")
      vals = s.expand_and_split(/\s+/, interpreter)
      loop = [var, vals, ""]
    elsif loop
      if l =~ /^\s*for\s+end\s*$/
        for v in loop[1]
          interpreter.variables.define_variable(loop[0], v)
          run_commands(loop[2], interpreter)
        end
        loop = false
      else
        loop[2] << l
      end
    elsif l =~ /^\s*([a-zA-Z0-9_-]+)\s*(\??)(=|:=)\s*(.*)/
      symbol = $1
      value = InterpreterString.parse_until_unquoted(StringIO.new($4),"\n", false)
      override = !($2 == '?')
      rec = (($3 == "=") ? nil : interpreter)
      
              
      interpreter.variables.define_variable(symbol, value, rec, override)
    elsif l =~ /^\s*#/
        # comment...
    else
      l += "\n"
      str = InterpreterString.parse_until_unquoted(StringIO.new(l),"\n")
      words = str.expand_and_split(/\s+/, interpreter)

      # Take care of strings starting with spaces...
      
      if words.size == 0
        next
      end
      
      symbol = words[0]
      all_args = words[1..-1]
      
      cmd = interpreter.get_command(symbol)

      args, opts = parse_args_and_opts(cmd, all_args)

      interpreter.context.parsing_file(symbol, io, lines_indices[idx]) # Missing line number
      interpreter.run_command(cmd, args, opts)
    end
  end
end
run_command_file(file, interpreter) click to toggle source

Runs a command file targeting the given interpreter.

# File lib/ctioga2/commands/parsers/file.rb, line 46
def run_command_file(file, interpreter)
  f = Utils::open(file)
  parse_io_object(f, interpreter)
end
run_commands(strings, interpreter) click to toggle source

Runs the given command strings

# File lib/ctioga2/commands/parsers/file.rb, line 52
def run_commands(strings, interpreter)
  io = StringIO.new(strings)
  parse_io_object(io, interpreter)
end

Protected Instance Methods

parse_args_and_opts(cmd, all_args) click to toggle source

Parses the all_args into arguments and options.

# File lib/ctioga2/commands/parsers/file.rb, line 194
def parse_args_and_opts(cmd, all_args)
  
  opts = {}
  args = []
  while all_args.size > 0
    a = all_args.shift
    if a =~ /^\/([a-zA-Z0-9_-]+)(=(.*))?$/
      o = $1
      if cmd.has_option?(o)
        if $2
          if $3
            opts[o] = $3
          else
            opts[o] = all_args.shift
          end
        else
          nxt = all_args.shift
          if ! nxt
            fatal { "Missing option text for option '#{o}'"}
          end
          if nxt =~ /^\s*=\s*$/
            nxt = all_args.shift
            if ! nxt
              fatal { "Missing option text for option '#{o}'"}
            end
            opts[o] = nxt
          else
            opts[o] = nxt.gsub(/^\s*=/,'')
          end
        end
      else
        warn { "#{o} looks like an option, but command #{cmd.name} does not have such an option" }
        args << a
      end
    else
      args << a
    end
  end

  return [args, opts]
end