Разбор массивов tcl в ruby ​​с верхушкой дерева - PullRequest
0 голосов
/ 15 марта 2010

У меня есть куча данных в (что я думаю, это) массив tcl. В основном это в форме {a {b c} d {e f} g}. Он вложен только в одну глубину, но не всегда вложен, то есть a может быть просто a или {aa bb} или {}, но никогда {aa {bb cc}}. Я хочу извлечь этот массив, чтобы использовать его в ruby.

Моей первой мыслью было: «Нет проблем, я напишу небольшую грамматику, чтобы разобрать это». Я установил древовидный гем и написал парсер, который, казалось, работал очень хорошо. У меня начались проблемы, когда я пытался извлечь массив из проанализированного дерева. Я хотел бы лучше понять причину проблем и что я делаю не так.

Вот мой код парсера: (tcl_array.treetop)

grammar TCLArray
  rule array
    "{" [\s]* "}" {
      def content
        []
      end
    }
    /
    "{" [\s]* array_element_list [\s]* "}" {
      def content
        array_element_list.content
      end
    }
  end

  rule array_element_list
    array_element {
      def content
        [array_element.content]
      end
    }
    /
    array_element [\s]+ array_element_list {
      def content
        [array_element.content] + array_element_list.content
      end
    }
  end

  rule array_element
    [^{}\s]+ {
      def content
        return text_value
      end
    }
    /
    array {
      def content
        array.content
      end
    }
  end
end

Вызов p.parse("{a}").content Выход tcl_array.rb:99:in 'content': undefined local variable or method 'array_element'

Первый член в массиве array_element_list (array_element) говорит о том, что array_element является неопределенной локальной переменной, но методы доступа должны автоматически определяться в соответствии с документацией к верхушке дерева.

Ранее я пробовал решение, основанное на грамматике с меньшим количеством, но несколько более сложных правил:

grammar TCLArray
  rule array
    "{" ([\s]* array_element ([\s]+ array_element)* )? [\s]* "}"
  end

  rule array_element
    [^{}\s]+ / array
  end
end

Но с этой грамматикой у меня были проблемы, когда парсер, казалось, создавал несколько различных выражений для правила массива, даже если он не использовал никаких альтернативных выражений (/). В результате я не мог понять, как получить доступ к различным битам правила массива, чтобы вернуть их как массив ruby.

Ответы [ 2 ]

2 голосов
/ 16 марта 2010

Возможно, генератор парсера в этом случае излишний. Вот простой свернутый вручную анализатор рекурсивного спуска на основе этого анализатора JSON Джеймса Эдварда Грея II :

#!/usr/bin/env ruby
# based on James Edward Gray II's solution to the Parsing JSON
#   Ruby Quiz #155: <http://RubyQuiz.Com/quiz155.html>

require 'strscan'

class TclArrayParser < StringScanner
  def parse
    parse_value
  ensure
    eos? or error "Unexpected data: '#{rest}'"
  end

  private

  def parse_value
    trim_space
    parse_string or parse_array
  ensure
    trim_space
  end

  def parse_array
    return nil unless scan(/\{\s*/)
    array = []
    while contents = parse_value
      array << contents
    end
    scan(/\}/) or error('Unclosed array')
    array
  end

  def parse_string
    scan(/[^{}[:space:]]+/)
  end

  def trim_space
    skip(/\s*/)
  end

  def error(message)
    pos = if eos? then 'end of input' else "position #{self.pos}" end
    raise ParseError, "#{message} at #{pos}"
  end

  class ParseError < StandardError; end
end

Вот тестовый набор:

require 'test/unit'
class TestTclArrayParser < Test::Unit::TestCase
  def test_that_an_empty_string_parses_to_nil
    assert_nil TclArrayParser.new('').parse
  end
  def test_that_a_whitespace_string_parses_to_nil
    assert_nil TclArrayParser.new("  \t  \n  ").parse
  end
  def test_that_an_empty_array_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new('{}').parse
  end
  def test_that_an_empty_array_with_whitespace_at_the_front_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new(' {}').parse
  end
  def test_that_an_empty_array_with_whitespace_at_the_end_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new('{} ').parse
  end
  def test_that_an_empty_array_with_whitespace_inside_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new('{ }').parse
  end
  def test_that_an_empty_array_surrounded_by_whitespace_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new(' {} ').parse
  end
  def test_that_an_empty_array_with_whitespace_at_the_front_and_inside_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new(' { }').parse
  end
  def test_that_an_empty_array_with_whitespace_at_the_end_and_inside_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new('{ } ').parse
  end
  def test_that_an_empty_array_surrounded_by_whitespace_with_whitespace_inside_parses_to_an_empty_array
    assert_equal [], TclArrayParser.new(' { } ').parse
  end
  def test_that_a_sole_element_parses
    assert_equal 'a', TclArrayParser.new('a').parse
  end
  def test_that_an_array_with_one_element_parses
    assert_equal ['a'], TclArrayParser.new('{a}').parse
  end
  def test_that_a_nested_array_parses
    assert_equal [[]], TclArrayParser.new('{{}}').parse
  end
  def test_that_a_nested_array_with_one_element_parses
    assert_equal [['a']], TclArrayParser.new('{{a}}').parse
  end
  def test_that_whitespace_is_ignored
    assert_equal [], TclArrayParser.new('     {     }     ').parse
  end
  def test_that_complex_arrays_parse_correctly
    assert_equal ['a', %w[b c], 'd', %w[e f], 'g'], TclArrayParser.new('{a {b c} d {e f} g}').parse
    assert_equal [%w[aa bb], %w[b c], 'd', %w[e f], 'g'], TclArrayParser.new('{{aa bb} {b c} d {e f} g}').parse
    assert_equal [[], %w[b c], 'd', %w[e f], 'g'], TclArrayParser.new('{{} {b c} d {e f} g}').parse
    assert_equal [[], ['b', 'c'], 'd', ['e', 'f'], 'g'], TclArrayParser.new("\n{\n{\n}\n{\nb\nc\n}\nd\n{\ne\nf\n}\ng\n}\n").parse
  end
end
0 голосов
/ 24 марта 2013

Отмечу это для справки, но я только что выпустил гем для анализа простого TCL.

https://github.com/julik/tickly

...