Pergunta

Eu tenho um monte de dados (o que eu acho) em uma matriz TCL. Basicamente, é na forma de {a {b c} d {e f} g}. Só está aninhado um profundamente, mas nem sempre é aninhado, ou seja, a pode ser apenas a ou pode ser {aa bb} ou possivelmente {}, mas nunca {aa {bb cc}}. Eu quero extrair essa matriz para que eu possa usá -la no Ruby.

Meu primeiro pensamento foi: "Não há problema, vou escrever uma pequena gramática para analisar isso". Eu instalei a gema da Treetop e escrevi um analisador, que parecia funcionar muito bem. Comecei a ter problemas quando tentei extrair uma matriz da árvore analisada. Eu gostaria de entender melhor a causa dos problemas e o que estou fazendo de errado.

Aqui está o meu código de analisador até agora: (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

Invocando p.parse("{a}").content rendimentos tcl_array.rb:99:in 'content': undefined local variable or method 'array_element'

O primeiro termo em Array_Element_List (Array_Element) diz que Array_Element é uma variável local indefinida, mas os métodos de acessórios devem ser definidos automaticamente de acordo com a documentação da Treetop.

Anteriormente, tentei uma solução baseada em uma gramática com menos regras, mas um pouco mais complicadas:

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

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

Mas com essa gramática, tive problemas em que o analisador parecia estar criando várias expressões diferentes para a regra da matriz, mesmo que não tenha usado nenhuma expressão alternativa (/). O resultado foi que eu não conseguia descobrir como acessar os vários bits da regra da matriz para devolvê -los como uma matriz de rubi.

Foi útil?

Solução

Talvez um gerador de analisador esteja exagerado neste caso. Aqui está um analisador de descendência recursiva simples e enrolada à mão com base em Este analisador JSON de James Edward Gray 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

Aqui está um testSuite:

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

Outras dicas

Observando isso para referência, mas acabei de lançar uma jóia para analisar o simples TCL.

https://github.com/julik/tickly

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top