Активация условных правил Whittle parser - PullRequest
1 голос
/ 13 декабря 2011

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

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

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

Код парсера

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 Ответ

1 голос
/ 13 декабря 2011

Я не верю, что поддержка приоритетов операторов играет здесь роль.Приоритеты операторов вступают в действие только при разрешении неоднозначностей в выражениях, подобных 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

Я думал о том, как обеспечить возможность сопоставления разных токенов в разных состояниях, но я опасаюсь написать клон lex / flex, который, я думаю, будет слишком запутанным, поэтому я пытаюсьпридумать подход, который использует блоки для вложения правил друг в друга, чтобы передать, как состояния связаны друг с другом;хотя не так просто создать легко понимаемый DSL, который делает это;) Я также хочу предоставить дополнительный DSL, чтобы скрыть алгоритм, используемый для повторения;возможно, предоставляя своего рода слой PEG, который трансформируется в LR-парсер.Это все еще (очень) молодой проект;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...