Ruby: использование StringScanner вызывает бесконечный цикл

У меня есть следующий класс:

require 'strscan'

class ConfParser
  include Enumerable

  class Error < StandardError; end
  VERSION = '0.0.1'
  SECTION_REGEX = /^\[       # Opening bracket
                   ([^\]]+)  # Section name
                   \]$       # Closing bracket
                 /x
  PARAMETER_REGEX = /^\s*([^:]+)  # Option
                      :
                      (.*?)$      # Value
                    /x

  attr_accessor :filename, :sections

  CONFIG_DIRECTORY = "./config"
  ENCODING = "UTF-8"

  def self.read(filename, opts = {})
    new(opts.merge(:filename => filename))
  end

  def initialize(opts = {})
    @filename = opts.fetch(:filename)
    @separator = opts.fetch(:separator, ":")
    @file = "#{CONFIG_DIRECTORY}/#{@filename}"
    @content = nil
    @config = Hash.new { |h,k| h[k] = Hash.new }

    load
  end

  def load
    raise_error("First line of config file contain be blank") if first_line_empty?

    f = File.open(@file, 'r')
    @content = f.read
    parse!

    ensure
      f.close if f && !f.closed?
  end

  def sections
    @config.keys
  end

  def [](section)
    return nil if section.nil?

    @config[section.to_s]
  end

  def []=( section, value )
    @config[section.to_s] = value
  end

  private

    def parse!
      @_section = nil
      @_current_line = nil
      property = ''
      string = ''

      @config.clear

      scanner = StringScanner.new(@content)

      until scanner.eos?
        @_current_line = scanner.check(%r/\A.*$/) if scanner.bol?

        if scanner.scan(SECTION_REGEX)
          @_section = @config[scanner[1]]
        else
          tmp = scanner.scan_until(%r/([\n"#{@param}#{@comment}] | \z | \\[\[\]#{@param}#{@comment}"])/mx)
          raise_error if tmp.nil?

          len = scanner[1].length
          tmp.slice!(tmp.length - len, len)

          scanner.pos = scanner.pos - len
          string << tmp
        end
      end

      process_property(property, string)

      logger @config
    end

    def process_property( property, value )
      value.chomp!
      return if property.empty? and value.empty?
      return if value.sub!(%r/\\\s*\z/, '')

      property.strip!
      value.strip!

      parse_error if property.empty?

      current_section[property.dup] = unescape_value(value.dup)

      property.slice!(0, property.length)
      value.slice!(0, value.length)

      nil
    end

    def logger log
      puts "*"*50
      puts log
      puts "*"*50
    end

    def first_line_empty?
      File.readlines(@file).first.chomp.empty?
    end

    def raise_error(msg = 'Error processing line')
      raise Error, "#{msg}: #{@_current_line}"
    end

    def current_section
      @_section ||= @config['header']
    end

end

Приведенный выше класс анализирует файлы, настроенные следующим образом:

[header]
project: Hello World
budget : 4.5
accessed :205

[meta data]
description : This is a tediously long description of the Hello World
  project that you are taking. Tedious isn't the right word, but
  it's the first word that comes to mind.

correction text: I meant 'moderately,' not 'tediously,' above.

[ trailer ]
budget:all out of budget.

Вы начинаете запускать его так:

require 'conf_parser'
cf = ConfParser.read "/path/to/conf/file"

По какой-то причине при запуске метода parse! происходит бесконечный цикл, и я не могу понять, почему. Любая причина, почему это происходит? Я никогда раньше не использовал StringScanner, так что это может быть мое незнание класса


person dennismonsewicz    schedule 31.12.2013    source источник


Ответы (1)


Рискуя констатировать очевидное, вы, скорее всего, никогда не удовлетворите scanner.eos?, что, в свою очередь, будет означать, что вы не продвигаете указатель сканирования до конца строки. Поскольку единственное изменение scanner.pos в ветви else ветви parse! заключается в уменьшении его (т. е. на len), это понятно. Если ветвь if не продвинет ее до конца, вы никогда не завершите работу.

person Peter Alfvin    schedule 31.12.2013
comment
Спасибо! Мне не хватало части моего оператора if, в котором пропускались определенные символы. - person dennismonsewicz; 31.12.2013