require 'dsl_string_helper_methods' module Dsl ReservedWords = ["and", "or"] attr_accessor :constraints def add_constraint(constraint) if constraint.is_a?(Constraint) self.class.combinators.each_pair do |key, value| if value.cache.size > 0 self.constraints << value.type.new((value.cache << constraint).collect {|c| c.is_a?(Constraint) ? c : nil}) value.cache = [] return end end self.constraints << constraint end end def self.included(klass) klass.extend(Dsl::ClassMethods) end module ClassMethods def evaluate(str) str.extend(Dsl::StringHelperMethods) Dsl::ReservedWords.each do |word| str.prefix_reserved_word_with_underscore!(word) end # Parenthesize methods calls to silence ruby warnings str.add_parenthesis! # Create a new instance and evaluate the given string inst = self.new inst.instance_eval str inst end def nouns(constraint_type, *args) args.each {|a| noun(constraint_type, a) } end def noun (constraint_type, n) n = escape_name(n) define_method(n) do |*constraint| # To allow default parameter of nil add_constraint(constraint[0]) return constraint_type.new(n) end end def terminators(*args) args.each {|a| terminator(a) } end def terminator(n) n = escape_name(n) define_method(n) do |*constraint| # To allow default parameter of nil add_constraint(constraint[0]) nil end end def modifier(name, attr, value) name = escape_name(name) define_method(name) do |constraint| (self.class.combinators["or"].cache + [constraint]).select {|x| x.respond_to?("default_#{attr}?".to_sym) && x.respond_to?("#{attr}=") && x.send("default_#{attr}?".to_sym)}.each {|x| x.send("#{attr}=", value) } constraint end end def bubble(name) name = escape_name(name) define_method(name) do |*constraint| constraint[0] end end def combinator(constraint_type, name) combinators[name] = Struct.new("Combinator", :type, :cache).new(constraint_type, []) define_method escape_name(name) do |constraint| self.class.combinators[name].cache << constraint nil end end def combinators @combinators ||= {} end protected def escape_name(name) ReservedWords.include?(name) ? "_#{name}" : name end end end