2015-09-03 3 views
3

Я реализую DSL, который имеет синтаксис:Treetop булево логические операции

"[keyword] or ([other keyword] and not [one more keyword])" 

Каждое ключевое слово превратится в булево (true, false) значение, после чего она должна быть рассчитана с помощью операторов and, or, not

Мой текущий грамматические правила соответствуют только строкам [keyword] or [other keyword] и терпят неудачу при укусах [keyword] or [other keyword] or [one more keyword]

Как написать грамматику, соответствующую любому количеству or, and конструкций?

Грамматика:

grammar Sexp 

    rule expression 
    keyword operand keyword <ExpressionLiteral> 
    end 

    rule operand 
    or/and <OperandLiteral> 
    end 

    rule or 
    'or' <OrLiteral> 
    end 

    rule and 
    'and' <AndLiteral> 
    end 

    rule keyword 
    space '[' ('\['/!']' .)* ']' space <KeywordLiteral> 
    end 

rule space 
    ' '* 
end 
end 

Обновления

Parser класс

class Parser 
    require 'treetop' 
    base_path = File.expand_path(File.dirname(__FILE__)) 
    require File.join(base_path, 'node_extensions.rb') 
    Treetop.load(File.join(base_path, 'sexp_parser.treetop')) 

    def self.parse(data) 
    if data.respond_to? :read 
     data = data.read 
    end 

    parser =SexpParser.new 
    ast = parser.parse data 

    if ast 
     #self.clean_tree(ast) 
     return ast 
    else 
     parser.failure_reason =~ /^(Expected .+) after/m 
     puts "#{$1.gsub("\n", '$NEWLINE')}:" 
     puts data.lines.to_a[parser.failure_line - 1] 
     puts "#{'~' * (parser.failure_column - 1)}^" 
    end 
    end 
    private 
    def self.clean_tree(root_node) 
     return if(root_node.elements.nil?) 
     root_node.elements.delete_if{|node| node.class.name == "Treetop::Runtime::SyntaxNode" } 
     root_node.elements.each {|node| self.clean_tree(node) } 
    end 
end 

tree = Parser.parse('[keyword] or [other keyword] or [this]') 
p tree 
p tree.to_array 

расширение узла

module Sexp 
    class KeywordLiteral < Treetop::Runtime::SyntaxNode 
    def to_array 
     self.text_value.gsub(/[\s\[\]]+/, '') 
    end 
    end 

    class OrLiteral < Treetop::Runtime::SyntaxNode 
    def to_array 
     self.text_value 
    end 
    end 

    class AndLiteral < Treetop::Runtime::SyntaxNode 
    def to_array 
     self.text_value 
    end 
    end 

    class OperandLiteral < Treetop::Runtime::SyntaxNode 
    def to_array 
     self.elements.map{|e| e.to_array} 
    end 
    end 

    class ExpressionLiteral < Treetop::Runtime::SyntaxNode 
    def to_array 
     self.elements.map{|e| e.to_array}.join(' ') 
    end 
    end 
end 
+0

My Treetop ржавый, но как насчет чего-то вроде выражения правила; выражение операнда слова/выражения; end'? Или выражается через повторение вместо рекурсии: выражение правила; ключевое слово (ключевое слово операнда) *; конец' –

+0

@ JörgWMittag не работает. Treetop ожидает места в позиции 'operator' в обоих примерах – djsmentya

+1

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

ответ

4

Ok, спасибо за это разъяснение. В Ruby «false», «true» или «true» истинно, потому что «и» сначала оценивается (имеет более высокий приоритет). Чтобы разобрать это, вам нужно одно правило для «или» списка (дизъюнкции), которое вызывает другое правило для списка «и» (союзы). Как это:

rule expression 
    s disjunction s 
    { def value; disjunction.value; end } 
end 

rule disjunction 
    conjunction tail:(or s conjunction s)* 
    { def value 
     tail.elements.inject(conjunction.value) do |r, e| 
     r or e.conjunction.value 
     end 
    end 
    } 
end 

rule conjunction 
    primitive tail:(and s primitive s)* 
    { def value 
     tail.elements.inject(primitive.value) do |r, e| 
     r and e.primitive.value 
     end 
    end 
    } 
end 

rule primitive 
    '(' expression ')' s { def value; expression.value; end } 
/
    not expression s { def value; not expression.value; end } 
/
    symbol s { def value; symbol.value; end } 
end 

rule or 
    'or' !symbolchar s 
end 

rule and 
    'and' !symbolchar s 
end 

rule not 
    'not' !symbolchar s 
end 

rule symbol 
    text:([[:alpha:]_] symbolchar*) s 
    { def value 
     lookup_value(text.text_value) 
    end 
    } 
end 

rule symbolchar 
    [[:alnum:]_] 
end 

rule s # Optional space 
    S? 
end 

rule S # Mandatory space 
    [ \t\n\r]* 
end 

Примечание некоторые вещи:

  • ключевые слова не должны быть немедленно следует символ символа. Для этого я использую негативный прогноз.
  • Главное правило потребляет ведущие пробелы, тогда почти каждое правило потребляет следующие пробелы (это моя политика). Вы должны проверить свою грамматику с минимальным и максимальным пробелом.
  • Нет необходимости использовать класс синтаксического класса для каждого правила, как и вы.
  • Вы можете увидеть, как продолжить шаблон для добавления, умножения и т. Д.
  • Я дал эскиз некоторого кода, который оценивает выражение. Используйте что-нибудь красивое, пожалуйста!
+0

" false or true and true "is true –

+1

Gah! Совершенно верно. Я отредактирую свое описание. Код по-прежнему правильный. – cliffordheath

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