2011-12-13 2 views
1

Я использую Whittle gem для анализа языка шаблона и хочу сопоставить все, что не содержится в правиле. Мне хорошо знакомы с другими шаблонами, но это скорее академические упражнения, чем случай производства.Активация условных правил синтаксического анализа

Проблемы я сталкиваюсь, что анализатор игнорирует приоритет :id выше :raw и все еще ждет для :raw тега после {{.

Как сообщить анализу, что в выражении не разрешено применять правило :raw и применять правило :spc внутри выражения?

Parser код

class Parser < Whittle::Parser 
    # Skip whitespaces (should not apply in :raw) 
    rule(:spc => /\s+/).skip! 

    # Various delimiters 
    rule("{{")^4 
    rule("}}")^4 
    rule("{%")^4 
    rule("%}")^4 
    rule("|")^4 
    rule("end")^4 

    # Defines an id (very large match) 
    rule(:id => /[a-zA-Z_.$<>=!:]+(\((\w+|\s+|,|")+\))?/)^2 

    # inline tag 
    rule(:inline) do |r| 
     r["{{", :inline_head, "}}"].as { |_,id,_| Tag::Inline.new(id) } 
    end 
    # inline tag contents 
    # allows "|" chaining 
    rule(:inline_head) do |r| 
     r[:inline_head, "|", :id].as { |head, _, id| head << id } 
     r[:id].as { |id| [id] } 
     r[].as { [] } 
    end 

    # block tag 
    rule(:block) do |r| 
     r["{%", :block_head, "%}", :all, "{%", "end", "%}"].as { |_,head,_,tags,_,_,_| 
      Tag::Block.new(head, tags) 
     } 
    end 
    # block tag heading 
    # separates all the keywords 
    rule(:block_head) do |r| 
     r[:block_head, :id].as { |head, id| head << id } 
     #r[:id].as { |id| [id] } 
     r[].as { [] } 
    end 

    # one rule to match them all 
    rule(:all) do |r| 
     r[:all,:inline].as { |all, inline| all << inline } 
     r[:all, :block].as { |all, block| all << block } 
     r[:all, :raw].as { |all, raw| all << raw } 
     r[].as { [] } 
    end 

    # the everything but tags rule 
    rule(:raw => /[^\{\}%]+/).as { |text| Tag::Raw.new(text) }^1 

    # starting rule 
    start(:all) 
end 

И вводимый текст будет и вывод является абстрактным синтаксическим деревом, представленным объектами (они просто хэш как объекты на данный момент).

<html> 
    <head> 
     <title>{{ title|capitalize }}</title> 
    </head> 
    <body> 
     <div class="news"> 
      {% for news in articles %} 
       {{ news.title }} 
       {{ news.body | limit(100) }} 
       {{ tags | join(",", name) }} 
      {% end %} 
     </div> 
    </body> 
</html> 

ответ

1

Я не верю, что поддержка приоритета оператора играет здесь определенную роль. Приоритеты операторов вступают в игру только при разрешении двусмысленностей в выражениях типа foo = 6 + 7, где выражение может быть интерпретировано как (foo = 6) + 7 или foo = (6 + 7). Предоставление не-операторам приоритета на самом деле не служит никакой цели.

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

Я думаю, вы действительно не хотите пропускать пробелы, так как это важно в вашей грамматике. Вы хотите включить его в свои правила, которые позволяют это; что сделает вашу грамматику более многословной, но (в настоящее время) неизбежной.

Так :raw становится чем-то вроде следующего, поглощая все пробельные и не синтаксические лексемы в одну строку:

rule(:raw => /[^\s\{\}%]+/) 

rule(:text) do |r| 
    r[:text, :raw].as { |text, raw| text << raw } 
    r[:text, :spc].as { |text, spc| text << spc } 
    r[:spc] 
    r[:raw] 
end 

Затем в :all правило, превратить этот текст в часть вашего AST (вы могли бы на самом деле делайте это и в приведенном выше правиле, но я ничего не знаю о ваших определениях классов).

rule(:all) do |r| 
    # ... snip ... 
    r[:all, :text].as { |all, text| all << Tag::Raw.new(text) } 
    # ... snip ... 
end 

Я думал о том, как обеспечить возможность сопрягать различные маркеры в пределах разных штатах, но я опасаться писать клон ЛЕКС/Flex, который я думаю, было бы слишком запутанным, поэтому я пытаюсь придумать подход, который использует блоки, чтобы встраивать правила друг в друга, чтобы передать, как состояния связаны друг с другом; хотя это не просто создать легко понять DSL, который это делает;) Я также хочу предоставить дополнительный DSL, чтобы скрыть алгоритм, используемый для повторения; возможно, предоставляя своего рода слой ПЭГ, который преобразуется в парсер LR. Это все еще (очень) молодой проект;)

+0

Woah от самого автора! Я пробую это как можно скорее. Спасибо –

+0

Работал и решил все мои проблемы после нескольких хитростей. Отличный проект! –

Смежные вопросы