################################################################################ # 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