Обновить
7
0
Andrey Fedotov@fischieye

Пользователь

Отправить сообщение
Да, спасибо, интересно! А в каком ваш Rly состоянии, достаточно stable для использования?
Когда мне нужно было сделать парсер, и я искал что-то по теме, находилось критически мало доступной информации с примерами по Racc, поэтому собственно и написал статью. А вот про ваш Rly вообще ничего не попалось.

Попробовал реализовать все то же самое на Rly.
Лексер делается проще, не вручную через StringScanner. Получился довольно компактный. Кажется, что так меньше тонкого контроля, но возможно он в большинстве случаев и не нужен, и все можно разрулить грамотными регулярными выражениями и их правильной очередностью.

require "rly"

class Lexer < Rly::Lex
  literals '{}(),;='
  ignore " \t\n"
  token :T_OBJECTID, /[\w\.]+:\"*\w*\"*:\w+/
  token :T_NUMBER, /\d+/
  token :T_NULL, /NULL(?=[^\w])/
  token :T_BOOLEAN, /[TF]{1}(?=[^\w])/
  token :T_IDENTIFIER, /\w+/
  token :T_STRING, /\".*?(?<!\\)\"/m
  token :T_REFERENCE, /\<.*?\>/
end


И если я конечно тут все правильно понял, то при создании парсера не очень понравилось, что для свертывания нетерминала задается только один блок, несмотря на наличие в правилах choise. И возможные варианты нужно разруливать в блоке самостоятельно, например при помощи case.

require "rly"

class Parser < Rly::Yacc

  rule 'input: object' do |*t|
    t[0].value = t[1].value
  end

  rule 'object: T_OBJECTID "{" attributes "}"' do |*t|
    oid = t[1].value.split(':')
    t[0].value = {
        :id => dequote(oid[0]),
        :name => convertNULL(dequote(oid[1])),
        :type => dequote(oid[2])
    }.merge(t[3].value)
  end

  rule 'attributes: attribute | attributes attribute' do |*t|
    case
      when t[1].type == :attribute
        t[0].value = t[1].value
      when t[1].type == :attributes
        t[0].value = t[1].value.merge(t[2].value)
    end
  end

  rule 'attribute: T_IDENTIFIER "=" value ";"' do |*t|
    t[0].value = {t[1].value => t[3].value}
  end

  rule 'values: value | values "," value' do |*t|
    case
      when t[1].type == :value
        t[0].value = [t[1].value]
      when t[1].type == :values
        t[0].value = t[1].value.concat([t[3].value])
    end
  end

  rule 'value: T_STRING |
               T_REFERENCE |
               T_NUMBER |
               T_BOOLEAN |
               T_NULL |
               "{" object "}" |
               "(" values ")"' do |*t|
    case
      when t[1].type == :T_STRING || t[1].type == :T_REFERENCE
        t[0].value = dequote(t[1].value)
      when t[1].type == :T_NUMBER
        t[0].value = t[1].value.to_i
      when t[1].type == :T_BOOLEAN
        t[0].value = t[1].value == 'T'
      when t[1].type == :T_NULL
        t[0].value = nil
      when t[2].type == :object || t[2].type == :values
        t[0].value = t[2].value
    end
  end

  def dequote(text)
    text.gsub(/\A["<]|[">]\Z/, '').strip
  end

  def convertNULL(text)
    text.upcase == "NULL" ? nil : text
  end

end

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Зарегистрирован
Активность