class Functor
attr_reader :val, :fun
def self.map_to_function filter, options_hash={}
cases = [[/^\/([0-9a-zA-Z@_.-?(){}*]+)\/$/, :regexp],
[/^#(\d+)$/, :idn],
[/^x(\d+)$/, :times],
[/^(\d+)$/,:specific],
[/^all$/, :all],
[/^(\d+)-(\d+)$/, :range],
[/^~(\d+)$/,:alternate],
[/^(.*)$/, :match]]
m, sym= nil, nil
cases.each do |ec|
next if options_hash[:exceptions] && options_hash[:exceptions].include?(ec[1])
m, sym = filter.match(ec[0]), ec[1]
break if m
end
return Functor.new(sym, m ? m.captures : nil, options_hash)
end
# these don't require :specific being attached the front. I.e. they let you return multiple items.
def self.specific_exceptions
[:alternate, :times, :all, :range, :specific]
end
# defaults are set up for Items
def initialize type, val, options={}
@options = {:name=>:name, :id=>:id}.merge(options)
@val = val
@fun = type
end
# call this functor with an array to query.
def call data_set
@fun ? self.send(@fun, data_set) : []
end
private
def range data_set
data_set[(@val[0].to_i-1..@val[1].to_i-1)] || []
end
def match data_set
data_set.select do |obj|
against = obj.send(@options[:name]).multi_args
found = false
against.each do |keyword|
found = true if keyword.downcase.start_with?(@val[0].downcase)
end
found
end
end
def idn data_set
data_set.select { |obj| obj.send(@options[:id]).to_s == @val[0]}
end
def specific data_set
[data_set[@val[0].to_i-1] || []].flatten
end
def times data_set
data_set[0..@val[0].to_i-1] || []
end
def alternate data_set
i = 0
data_set.select { |obj|
i += 1
i % @val[0].to_i == 0
}
end
def regexp data_set
begin
exp = Regexp.new(@val[0])
data_set.select{ |obj| obj.send(@options[:name]).match(exp) }
rescue
[]
end
end
def all data_set
data_set
end
end
# the concept is we parse what we want from the string passed.
# We return back the items found from a given list.
def query_parse str, main_list, destroy=false, options={}
valid_commands = ["and", "or", "from"]
# generates an array of possible commands.
commands = str.squeeze(" ").multi_args
return [] if commands.empty? || commands[0].empty?
# Always starts with "and" command
full_object_query = ["and", commands.shift]
# collects one and only one query phrase.
loop do
break if !commands[0] || commands[0].empty? || !valid_commands.include?(commands[0])
# if it's from, link it with the previous object.
if commands[0] == "from"
break if !full_object_query[-1]
full_object_query[-1] = "#{full_object_query[-1]} #{commands.shift} #{commands.shift}"
else
(full_object_query << commands.shift(2)).flatten!
end
end
# destroys what we got from the original string if destroy set to true.
if destroy
full_object_query.each do |each_query|
str.sub!(each_query.to_s, "")
end
str.strip!
end
found_so_far = []
loop do
query_comm = full_object_query.shift(2)
break if !query_comm[0] || !query_comm[1]
case query_comm[0]
when "and" then found_so_far.concat(query_list(query_comm[1], main_list, options)) # query the objects in the room with the arg
when "or" then found_so_far.concat(query_list(query_comm[1], main_list, options)) if found_so_far.empty?
end
end
found_so_far.uniq!
return found_so_far
end
# query a list (designed for objects)
# example: query_list "2.sword", inventory_of_items
# list is a hash of identifiers and valid lists.
def query_list str, list, options_hash = {}
found = [] # returned at the end with a grand list of the items found.
# first split by space.
from_list = str.split ' from '
from_list.reverse!
next_iteration_list = []
if list.is_a? Array
next_iteration_list = list
elsif list.is_a? Hash
if from_list.count > 1
which_keys = query_list(from_list[0], list.keys, {:name=>:to_s, :id=>:__id__, :exceptions=>[:all, :specific, :alternative, :regexp, :times, :idn]})
else
which_keys = []
end
if which_keys.empty?
list.each_pair do |p|
next_iteration_list.concat p[1]
end
else
which_keys.each do |key|
next_iteration_list.concat list[key]
end
from_list.shift
end
end
# for each_query in list we need to execute a query on it and hand off the resultant to the next query.
from_list.each do |each_query|
s = each_query.split '.'
funs = []
s.each do |filter|
funs.unshift Functor.map_to_function(filter, options_hash)
end
funs.push Functor.new(:specific, 1, options_hash) if !Functor.specific_exceptions.include? funs[-1].fun
funs.each do |functor|
next_iteration_list = functor.call next_iteration_list
end
found = next_iteration_list
next_iteration_list = []
found.each do |each_found|
next_iteration_list.concat(each_found.stuff) if each_found.respond_to? :stuff
end
end
return found
end