################################################################################
# myagent.rb is self-modifying code. It is a framework for creating new agents.
#
# To run:
#
# C:\trane\myagent>ruby myagent.rb
# > hello
# Default response.
#
# > add pattern /^hello/, hello
#
#
# > hello
# Default response from hello
#
# > > def hello(input=nil, pattern=nil)
# > > if @count == nil
# > > @count = 1; return 'Hello. I am ready for my first lesson.'
# > > end
# > > return 'Hello again.'
# > > end # method_def
#
#
# > hello
# Hello. I am ready for my first lesson.
#
# > hi is a synonym for hello
# OK, (?-mix:^hi) has been added, and will now call hello.
#
# > hi
# Hello again.
#
# >
################################################################################
require 'yaml'
class MyAgent
def initialize
@filename = File.expand_path(File.dirname(__FILE__)) + "/patterns.yaml"
unless load_message = load_synonyms
@patterns = [ [/^(.*?) is a synonym for (.*)/, :add_synonym],
[/^save syn/, :save_synonyms],
[/^load syn/, :load_synonyms],
[/^add pattern (.*), (.*)/, :add_pattern],
[/^>(.*)/, :defining_method]
]
save_synonyms # creates patterns.yaml on first use.
else
puts load_message
end
@method_def = ''
end
def def_method method_def
file = IO.read(__FILE__)
return "Doesn't have 'end # class'" unless file =~ /(.*)end # class(.*)if \$0 == __FILE__(.*)/m
grp1 = $1.to_s
grp2 = $2.to_s
grp3 = $3.to_s
new_filename = get_new_filename
File.open(new_filename, "w+") { |f|
f.puts grp1 + method_def + "\n\nend # class\n\nif $0 == __FILE__" + grp3
}
@patterns.push( [@new_regexp, @new_symbol] )
save_synonyms
MyAgent.class_eval(method_def) # not working when client code below is left uncommented out.
end
# If this file has a '.' + a number before the '.rb' extension
# (i.e. foo.0.rb), increment the number (i.e. return foo.1.rb).
# Otherwise return the name of this file unchanged.
def get_new_filename
old_filename = __FILE__
if old_filename =~ /(.*)\.(\d+)\.rb$/
incr = $2.to_i + 1
return new_filename = $1.to_s + "." + incr.to_s + ".rb"
end
return old_filename # Dangerous!
end
# Match input against each regexp pattern in @patterns;
# if there's a match, call the method associated with the pattern.
def getResponse input
r = "Default response."
@patterns.each { |pattern|
if input =~ pattern[0]
return self.send(pattern[1], pattern[0], input) rescue return r
end
}
return r
end
def load_synonyms(pattern=nil, input=nil)
if File.exist?(@filename)
@patterns = YAML.load_file(@filename)
return "I have loaded #{@filename}."
end
return false
end
def save_synonyms(pattern=nil, input=nil)
r = "Eep! error saving synonyms! "
begin
File.open(@filename, 'w') do |out|
YAML.dump(@patterns, out)
end
return "OK I have saved the synonyms to disk."
rescue => error
return r << error
end
end
def add_synonym(pattern, input)
return "Problem matching pattern." unless input =~ pattern
grp1 = %r{^#{$1}}
grp2 = %r{^#{$2}}
method =''
@patterns.each { |p|
if p[0] == grp2
method = p[1]
break
end
}
return unless method.to_s != nil
@patterns.push( [grp1, method] )
save_synonyms
return "OK, #{grp1} has been added, and will now call #{method}."
end
def add_pattern(pattern=nil, input=nil)
if input =~ pattern
@new_regexp = $1.to_s
@new_symbol = $2.to_s
@new_regexp.gsub!(/\//, '')
@new_regexp = %r{#{@new_regexp}}
method_def = " def #{@new_symbol}(pattern=nil, input=nil)\n return 'Default response from " + @new_symbol + "'\n end # method_def"
return def_method(method_def)
else
return "#{input} doesn't match #{pattern}."
end
end
def defining_method(pattern=nil, input=nil)
if input =~ pattern
@method_def << "\n " << $1.to_s
if @method_def =~ /end # method_def/
return def_method(@method_def)
end
return ''
else
return "#{input} doesn't match #{pattern}."
end
end
def method_missing input, *rest
return getResponse(input.to_s)
end
end # class
if $0 == __FILE__
bot = MyAgent.new
print "\n> "; $stdout.flush
while (line = gets) !~ /^:q/ and line !~ /^$/
print bot.send(line.to_s).to_s + "\n\n"
print "> "; $stdout.flush
end
end # of file