如何在Parslet中拆分原子?

时间:2014-04-17 19:04:33

标签: ruby arrays parsing parslet

我正在构建类似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的值拆分为数组?

1 个答案:

答案 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}