Поддерево Parslet не стреляет

Возобновить (я сократил следующую длинную историю до простой проблемы)

tree = {:properties => [{:a => 'b'}, {:c => 'd'}]}
big_tree = {:properties => [{:a => 'b'}, {:c => 'd'}], :moves => [{:a => 'b'}, {:c => 'd'}]}

trans = Parslet::Transform.new do
    rule(:properties => subtree(:nested)) do
        out = {}
        nested.each {|pair| out = out.merge pair}
        {:properties => out}
    end
end

pp tree
pp trans.apply(tree)
pp big_tree
pp trans.apply(big_tree)

# OUTPUT

{:properties=>[{:a=>"b"}, {:c=>"d"}]}
{:properties=>{:a=>"b", :c=>"d"}} # Worked with small tree
{:properties=>[{:a=>"b"}, {:c=>"d"}], :moves=>[{:a=>"b"}, {:c=>"d"}]}
{:properties=>[{:a=>"b"}, {:c=>"d"}], :moves=>[{:a=>"b"}, {:c=>"d"}]} # Didn't work with bigger tree

========================= ПОЛНАЯ ИСТОРИЯ (Не так актуально после заголовка)

Я делаю парсер файлов SGF с помощью Parslet.

Сейчас я на стадии, чтобы сделать Трансформер.

От парсера я уже получаю такие структуры:

[{:properties=>
   [{:name=>"GM"@2, :values=>[{:value=>"1"@5}]},
    {:name=>"FF"@7, :values=>[{:value=>"4"@10}]},
    {:name=>"SZ"@12, :values=>[{:value=>"19"@15}]},
    {:name=>"AP"@18, :values=>[{:value=>"SmartGo Kifu:2.2"@21}]},
    {:name=>"GN"@40, :values=>[{:value=>"2013-05-11g"@43}]},
    {:name=>"PW"@57, :values=>[{:value=>"Dahan"@60}]},
    {:name=>"PB"@68, :values=>[{:value=>"SmartGo"@71}]},
    {:name=>"DT"@81, :values=>[{:value=>"2013-05-11"@84}]},
    {:name=>"KM"@97, :values=>[{:value=>"6.5"@100}]},
    {:name=>"RE"@106, :values=>[{:value=>"W+R"@109}]},
    {:name=>"RU"@115, :values=>[{:value=>"AGA (Area)"@118}]},
    {:name=>"ID"@129, :values=>[{:value=>"ch0"@132}]}],
  :moves=>
   [{:player=>"B"@137, :place=>"oq"@139},
    {:player=>"W"@143, :place=>"dd"@145},
    {:player=>"B"@149, :place=>"oo"@151},
    ...etc...

Набор правил, который я использую для преобразования:

    # Rewrite player: COLOR, place: X to COLOR: X
    rule( player: simple(:p), place: simple(:pl)) do
        if p == 'W'
            { white: pl }
        elsif p == 'B'
            { black: pl }
        end
    end
    # Un-nest single-value hash
    rule( value: simple(:v)) { v }
    # Rewrite name: KEY, values: SINGLE_VALUE to KEY: SINGLE_VALUE
    rule( name: simple(:n), values: [ simple(:v) ]) { {n.to_sym => v} }
    # A Problem!!!
    rule( properties: subtree(:props) ) do
        out = {}
        props.each {|pair| pair.each {|k, v| out[k] = v}}
        { properties: out }
    end

С такими правилами я получаю следующую структуру:

[{:properties=>
   [{:GM=>"1"@5},
    {:FF=>"4"@10},
    {:SZ=>"19"@15},
    {:AP=>"SmartGo Kifu:2.2"@21},
    {:GN=>"2013-05-11g"@43},
    {:PW=>"Dahan"@60},
    {:PB=>"SmartGo"@71},
    {:DT=>"2013-05-11"@84},
    {:KM=>"6.5"@100},
    {:RE=>"W+R"@109},
    {:RU=>"AGA (Area)"@118},
    {:ID=>"ch0"@132}],
  :moves=>
   [{:black=>"oq"@139},
    {:white=>"dd"@145},
    {:black=>"oo"@151},
    ...etc...

Все отлично. Единственная моя проблема заключается в том, что: свойства Array of Hashes.

В конце концов я хочу иметь

[{:properties=>
   {:GM=>"1"@5,
    :FF=>"4"@10,
    :SZ=>"19"@15,
    :AP=>"SmartGo Kifu:2.2"@21,
    :GN=>"2013-05-11g"@43,
    :PW=>"Dahan"@60,
    :PB=>"SmartGo"@71,
    :DT=>"2013-05-11"@84,
    :KM=>"6.5"@100,
    :RE=>"W+R"@109,
    :RU=>"AGA (Area)"@118,
    :ID=>"ch0"@132},
  :moves=>
   [{:black=>"oq"@139},
    {:white=>"dd"@145},
    {:black=>"oo"@151},
    ...etc...

Ты видишь? Объедините все массивные хэши внутри: properties, потому что после предыдущих преобразований у них теперь есть уникальные ключи. Также немного сгладьте структуру.

Привет! Я могу сделать это вручную. Я имею в виду запустить отдельный метод, как

merged_stuff = {}
tree.first[:properties].each {|pair| pair.each {|k, v| merged_stuff[k] = v}}
tree.first[:properties] = merged_stuff

Но почему я не могу сделать это с аккуратными правилами преобразования, чтобы все логики преобразования были в одном месте?

Дело в том, что rule( properties: subtree(:props) ) не уволен вообще. Даже если я просто верну ноль из блока, это ничего не изменит. Так, кажется, что это subtree не ловит вещи, или я не ловлю.

2 ответа

Решение

Проблема в том, что :properties а также :moves ключи в том же хеше, и subtree очевидно, не хочет совпадать с частью хэша. Если вы удалите :moves, правило будет выполнено. Это как-то объясняется в документации:

Слово о моделях

Учитывая хеш PORO

{ 
  :dog => 'terrier', 
  :cat => 'suit' }

можно предположить, что следующее правило соответствует :dog и заменяет его на 'foo':

rule(:dog => 'terrier') { 'foo' }

Это, честно говоря, невозможно. Как бы 'foo' жить кроме :cat => 'suit' внутри хеша? Это не может. Вот почему хэши либо полностью совпадают, либо вообще не совпадают.

хотя я должен признать, что это не очень четкий пример.

Таким образом, правило проблемы должно выглядеть так:

rule( properties: subtree(:props), moves: subtree(:m) ) do
    out = {}
    props.each {|pair| pair.each {|k, v| out[k] = v}}
    { properties: out , moves: m}
end

Правила преобразования соответствуют целому узлу и заменяют его, поэтому вам нужно сопоставить весь хэш, а не только один ключ.

rule( properties: subtree(:props),  moves: subtree(:moves) ) 

Если вы преобразовали {:name=>"GM", :values=>[{:value=>"1"}]} вводить вещи в объекты (скажем, используя OpenStruct), тогда вам не нужно использовать subtree, ты можешь использовать sequence,

Другие вопросы по тегам