Pull to refresh

Comments 4

Синтаксис Racc несколько «не рубишен». Я как-то сел портировать питоновский Ply (который очень хорош по читабельности) на Ruby. Результат можно посмотреть тут: github.com/farcaller/rly

В целом, я старался оптимизировать код, но лексер несколько медленный, так как на регулярках (хотя в ruby2.0 он заметно шустрее). Если интересно – я могу детальнее описать механизм работы и подводные камни LALR(1)-генераторов на руби.
Да, спасибо, интересно! А в каком ваш 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
> что для свертывания нетерминала задается только один блок, несмотря на наличие в правилах choise

можно просто сделать несколько правил.

Я недавно выкатил 0.2.3, там несколько «хелперов» для типичных задач свертки + альтернативный вариант блоков (в аргументах – value, результат присваивается в lhs.value).

Парсер условно стабилен. В том плане, что он делает то что мне нужно на тех грамматиках, на которых я его гоняю (c, tablegen). Естественно, это 0.2.3, так что баги там, вероятно, есть. Так что багрепорты (особенно с патчами) принимаются на ура.
И да, мне кажется корректнее, если «типизацию» значения (например приведение значения токена в int) должен делать лексер. Фактически, я еще ни разу не использовал свойство *Token#type в парсере.
Sign up to leave a comment.

Articles

Change theme settings