我正在构建类似SQL的查询语言。我希望能够处理逗号分隔的项目列表。我用这段代码成功实现了这个目标:
class QueryParser < Parslet::Parser
rule(:space) { match('\s').repeat(1) }
rule(:space?) { space.maybe }
rule(:delimiter) { space? >> str(',') >> space? }
rule(:select) { str('SELECT') >> space? }
rule(:select_value) { str('*') | match('[a-zA-Z]').repeat(1) }
rule(:select_arguments) do
space? >>
(select_value >> (delimiter >> select_value).repeat).maybe.as(:select) >>
space?
end
rule(:from) { str('FROM') >> space? }
rule(:from_arguments) { match('[a-zA-Z]').repeat(1).as(:from) >> space? }
rule(:query) { select >> select_arguments >> from >> from_arguments }
root(:query)
end
SELECT id,name,fork FROM forks
之类的内容正确输出{:select=>"id,name,fork"@7, :from=>"forks"@25}
树。
现在,我希望能够将SELECT
个参数(在这种情况下为id,name,fork
)转换为数组,而不是稍后再讨论这个问题。我可以通过运行'id,name,fork'.split ','
来完成此操作。在应用时,我无法让Parslet变压器为我这样做。这是我的查询变换器的代码:
class QueryTransformer < Parslet::Transform
rule(select: simple(:args)) { args.split(',') }
end
如此应用:
QueryTransformer.new.apply(
QueryParser.new.parse('SELECT id,name,fork FROM forks')
)
结果与我没有应用时相同:{:select=>"id,name,fork"@7, :from=>"forks"@25}
。
我希望:select
的值是一个像["id","name","fork"]
这样的数组。
我的问题是:如何使用变换器将:select
的值拆分为数组?
答案 0 :(得分:2)
你需要把&#34; as(:xxx)&#34;在你希望以后能够玩的解析树的任何部分。
我在此更改了您的rule(:select_value)
以将值记为 a :value
rule(:select_value) { (str('*') | match('[a-zA-Z]').repeat(1)).as(:value) }
现在你的解析器输出:
{:select=>[{:value=>"id"@7}, {:value=>"name"@10}, {:value=>"fork"@15}], :from=>"forks"@25}
使用以下方法很容易转换:
class QueryTransformer < Parslet::Transform
rule(:value => simple(:val)) { val }
end
然后你得到:
{:select=>["id"@7, "name"@10, "fork"@15], :from=>"forks"@25}
所以完整的代码如下: -
require 'parslet'
class QueryParser < Parslet::Parser
rule(:space) { match('\s').repeat(1) }
rule(:space?) { space.maybe }
rule(:delimiter) { space? >> str(',') >> space? }
rule(:select) { str('SELECT') >> space? }
rule(:select_value) { (str('*') | match('[a-zA-Z]').repeat(1)).as(:value) }
rule(:select_arguments) do
space? >>
(select_value >> (delimiter >> select_value).repeat).maybe.as(:select) >>
space?
end
rule(:from) { str('FROM') >> space? }
rule(:from_arguments) { match('[a-zA-Z]').repeat(1).as(:from) >> space? }
rule(:query) { select >> select_arguments >> from >> from_arguments }
root(:query)
end
puts QueryParser.new.parse('SELECT id,name,fork FROM forks')
# => {:select=>[{:value=>"id"@7}, {:value=>"name"@10}, {:value=>"fork"@15}], :from=>"forks"@25}
class QueryTransformer < Parslet::Transform
rule(:value => simple(:val)) { val }
end
puts QueryTransformer.new.apply(
QueryParser.new.parse('SELECT id,name,fork FROM forks')
)
# => {:select=>["id"@7, "name"@10, "fork"@15], :from=>"forks"@25}