解析TCL阵列中的红宝石与树梢
题
我有一堆中的数据(我认为是)一个Tcl阵列。基本上它是在{a {b c} d {e f} g}
的形式。这只是一个嵌套深,但并不总是嵌套,也就是说,a
可能只是a
或者可以{aa bb}
或可能{}
,但从来没有{aa {bb cc}}
。我想提取这个数组,所以我可以在红宝石使用它。
我的第一个念头是,“没问题,我会写一点点的语法来分析这一点。”我安装了树梢的宝石,并写了一个解析器,这似乎工作得很好。我开始有问题,当我试图提取解析树的数组。我想更好地了解这些问题的原因,是我做错了。
下面是我的解析器代码到目前为止:(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
但有了这个语法我哪里解析器似乎创造,即使它没有使用任何替代表达式(/)排列规则的几个不同表情的问题。其结果是,我无法找出如何访问阵列规则的各个位将其返回作为红宝石阵列。
解决方案
也许解析器生成是矫枉过正在这种情况下。下面是基于由詹姆斯·爱德华·格雷II 这JSON解析器简单手卷递归下降解析器:
#!/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
其他提示
注意到此作为参考,但我刚刚发布了用于解析简单TCL宝石。
不隶属于 StackOverflow