djsmentya
djsmentya

Reputation: 315

Treetop boolean logic operations

I am implementing DSL which has syntax:

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

Each keyword will transform to boolean (true, false) value and after that it should be calculated using operators and, or, not

My current grammar rules match only strings [keyword] or [other keyword] and fails on stings [keyword] or [other keyword] or [one more keyword]

How to write grammar that match any ammount of or , and constructions?

Grammar:

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

Updates

Parser class

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

node extension

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

Upvotes: 3

Views: 388

Answers (1)

cliffordheath
cliffordheath

Reputation: 2606

Ok, thanks for that clarification. In Ruby, "false and true or true" is true, because the "and" is evaluated first (it has higher precedence). To parse this, you need one rule for the "or" list (the disjunctions) which calls another rule for the "and" list (the conjunctions). Like this:

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

Note some things:

  • keywords must not be immediately followed by a symbol character. I use negative lookahead for this.
  • The top rule consumes leading whitespace, then almost every rule consumes following whitespace (this is my policy). You should test your grammar with minimum and maximum whitespace.
  • There's no need to use a syntaxnode class on every rule, as you did.
  • You can see how to continue the pattern for addition, multiplication, etc
  • I've given a sketch of some code that evaluates the expression. Use something prettier please!

Upvotes: 4

Related Questions