2010-03-15 42 views
0

我有一堆數據(我認爲是)一個tcl數組。基本上它的形式是{a {b c} d {e f} g}。它只嵌套一個深度,但不總是嵌套,也就是說,a可能只是a或它可能是{aa bb}或可能{},但從來沒有{aa {bb cc}}。我想提取這個數組,以便我可以在ruby中使用它。用樹頂解析tcl數組在紅寶石上

我的第一個想法是,「沒問題,我會寫一點語法來解析這個。」我安裝了樹梢寶石,並寫了一個解析器,這似乎工作得很好。當我嘗試從解析樹中提取數組時,我開始出現問題。我想更好地瞭解問題的原因以及我做錯了什麼。

這裏是我的解析器代碼至今:(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是一個未定義的局部變量,但訪問器方法應該根據treetop文檔自動定義。

早些時候,我想這是基於了與較少但稍微複雜規則語法的解決方案:

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

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

但有了這個語法我哪裏解析器似乎要創造幾個不同的表達問題數組規則即使沒有使用任何替代表達式(/)。結果是我無法弄清楚如何訪問數組規則的各個位以將它們返回爲一個紅寶石數組。

回答

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,並寫一些輸出數組的東西,紅寶石可以理解,但我認爲這是一個機會,稍微回顧一下解析器。因此,我仍然很想知道我做錯了什麼。我不知道這是Treetop的缺陷還是我對它的理解有缺陷。 無論如何,非常感謝您的辛勤工作! – enki 2010-03-17 07:36:21