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
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:
Note some things: