2010-03-15 4 views
0

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

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

Вот мой анализатор кода до сих пор: (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

Возможно, генератор синтаксического анализа является излишним в этом случае. Вот простой скрученные вручную рекурсивный спуск парсера на основе this JSON parser by 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 

Вот Тесты:

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

Вы обеспечили восхитительное решение проблемы под руку, так что большое спасибо. :) Сказанное, простейшим решением, вероятно, было бы узнать немного TCL и написать что-то, чтобы вывести массив в нечто, что мог бы понять рубин, но я видел это как возможность немного переосмыслить парсеры. Поэтому мне все еще интересно узнать, что я делаю неправильно. Я не могу сказать, является ли это дефектом в Тритопе или дефектом в моем понимании этого. В любом случае, большое спасибо за вашу тяжелую работу! – enki

Смежные вопросы