Как мне разделить атом в Парслете?

Я строю 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 ',', Я не могу заставить трансформатор Парслета сделать это для меня при применении. Это мой код для моего преобразователя запросов:

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 ответ

Решение

Вам нужно поместить "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}
Другие вопросы по тегам