Да, спасибо, интересно! А в каком ваш Rly состоянии, достаточно stable для использования?
Когда мне нужно было сделать парсер, и я искал что-то по теме, находилось критически мало доступной информации с примерами по Racc, поэтому собственно и написал статью. А вот про ваш Rly вообще ничего не попалось.
Попробовал реализовать все то же самое на Rly.
Лексер делается проще, не вручную через StringScanner. Получился довольно компактный. Кажется, что так меньше тонкого контроля, но возможно он в большинстве случаев и не нужен, и все можно разрулить грамотными регулярными выражениями и их правильной очередностью.
И если я конечно тут все правильно понял, то при создании парсера не очень понравилось, что для свертывания нетерминала задается только один блок, несмотря на наличие в правилах 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
Такое уже сделано:
Статья в журнале Make:
https://makezine.com/projects/smart3-the-self-solving-rubiks-cube/
Когда мне нужно было сделать парсер, и я искал что-то по теме, находилось критически мало доступной информации с примерами по Racc, поэтому собственно и написал статью. А вот про ваш Rly вообще ничего не попалось.
Попробовал реализовать все то же самое на Rly.
Лексер делается проще, не вручную через StringScanner. Получился довольно компактный. Кажется, что так меньше тонкого контроля, но возможно он в большинстве случаев и не нужен, и все можно разрулить грамотными регулярными выражениями и их правильной очередностью.
И если я конечно тут все правильно понял, то при создании парсера не очень понравилось, что для свертывания нетерминала задается только один блок, несмотря на наличие в правилах choise. И возможные варианты нужно разруливать в блоке самостоятельно, например при помощи case.