1

我正在构建一种类似 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使用转换器将值拆分为数组?

4

1 回答 1

1

您需要将“as(:xxx)”放在您希望稍后能够使用的解析树的任何部分。

在这里,我更改了您以将rule(:select_value)这些值记住: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}
于 2014-04-18T06:14:51.943 回答