Click here to Skip to main content
Click here to Skip to main content
Articles » Languages » C# » General » Downloads
 
Add your own
alternative version

Building an AI Chatbot using a Regular Expression Engine

, 21 Jun 2007
This article describes how to build an AI Chatterbot using a popular, Regular Expression-based open source Chatterbot engine: Verbots
verbotsdk.zip
ResourceFiles
bin
Debug
ResourceFiles.dll
ResourceFiles.pdb
Release
ResourceFiles.dll
CHANGELOG
COPYING
mssccprj.scc
obj
Debug
buildinfo.inf
ResourceFiles.dll
ResourceFiles.pdb
ResourceFiles.projdata
temp
TempPE
Release
buildinfo.inf
ResourceFiles.dll
ResourceFiles.projdata
temp
TempPE
ResourceFiles.csproj.user
ResourceFiles.csproj.vspscc
vssver.scc
vssver2.scc
Verbot4Library
bin
Debug
Verbot4Library.dll
Verbot4Library.pdb
Release
Verbot4Library.dll
mssccprj.scc
obj
Debug
buildinfo.inf
temp
TempPE
Verbot4Library.dll
Verbot4Library.dll.incr
Verbot4Library.pdb
Verbot4Library.projdata
Release
buildinfo.inf
temp
TempPE
Verbot4Library.dll
Verbot4Library.projdata
Verbot4Library.csproj.user
Verbot4Library.csproj.vspscc
vssver.scc
vssver2.scc
VerbotConsoleApplication
App.ico
bin
Debug
Verbot4Library.dll
VerbotConsoleApplication.exe
VerbotConsoleApplication.pdb
Release
Verbot4Library.dll
VerbotConsoleApplication.exe
mssccprj.scc
obj
Debug
buildinfo.inf
temp
TempPE
VerbotConsoleApplication.exe
VerbotConsoleApplication.pdb
VerbotConsoleApplication.projdata
Release
buildinfo.inf
temp
TempPE
VerbotConsoleApplication.exe
VerbotConsoleApplication.projdata
VerbotConsoleApplication.csproj.user
VerbotConsoleApplication.csproj.vspscc
vssver.scc
VerbotWindowsApplicationSample
App.ico
bin
Debug
Verbot4Library.dll
Verbot4Library.pdb
VerbotWindowsApplicationSample.exe
VerbotWindowsApplicationSample.pdb
Release
Verbot4Library.dll
VerbotWindowsApplicationSample.exe
mssccprj.scc
obj
Debug
buildinfo.inf
temp
TempPE
VerbotWindowsApplicationSample.exe
VerbotWindowsApplicationSample.pdb
VerbotWindowsApplicationSample.projdata
Release
buildinfo.inf
temp
TempPE
VerbotWindowsApplicationSample.exe
VerbotWindowsApplicationSample.projdata
VerbotWindowsApplicationSample.VerbotWinApp.resources
Resources
sample.ckb
sample.vkb
vssver2.scc
VerbotWindowsApplicationSample.csproj.user
VerbotWindowsApplicationSample.csproj.vspscc
vssver.scc
vssver2.scc
/*
	Copyright 2004-2006 Conversive, Inc.
	http://www.conversive.com
	3806 Cross Creek Rd., Unit F
	Malibu, CA 90265
 
	This file is part of Verbot 4 Library: a natural language processing engine.

    Verbot 4 Library is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Verbot 4 Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Verbot 4 Library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
	
	Verbot 4 Library may also be available under other licenses.
*/

using System;
using System.Collections;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace Conversive.Verbot4
{
	/// <summary>
	/// Toolbox for dynamically compiling and executing C# code.
	/// </summary>
	public class CSharpToolbox
	{
		public bool ContainsCode
		{
			get
			{
				bool bRet = true;
				if(this.codeModules.Count == 0 && this.conditions.Count == 0 && this.outputs.Count == 0)
					bRet = false;
				return bRet;
			}
		}

		private string COND_PREFIX = "Cond_";
		private string OUTPUT_PREFIX = "Output_";

		private ArrayList codeModules;//list of CodeModule objects
		public ArrayList CodeModules
		{
			get{return this.codeModules;}
			set{this.codeModules = value;}
		}
		private Hashtable conditions;//keys are ids, values are condition strings
		private Hashtable outputs;//keys are ids, values are output strings

		private string code;
		public string Code{get{return this.code;}}
		
		string openTag = "<?csharp";
		string closeTag = "?>";

		private Assembly assembly;

		private Hashtable threadJobs;//keys are Thread objects, values are jobs or results

		public delegate void CompileWarning(string warningText, string lineText);
		public event CompileWarning OnCompileWarning;
		public delegate void CompileError(string errorText, string lineText);
		public event CompileError OnCompileError;

		public CSharpToolbox()
		{
			this.codeModules = new ArrayList();
			this.conditions = new Hashtable();
			this.outputs = new Hashtable();
			this.assembly = null;
			this.threadJobs = new Hashtable();
		}

		public void AddCodeModule(CodeModule cm)
		{
			this.codeModules.Add(cm);
		}

		public void AddCondition(string id, string condition)
		{
			this.conditions[id] = condition;
		}

		public bool ContainsCSharpTags(string text)
		{
			//need to see escaped csharp tags as csharp tags
			//so that we can remove the backslashes later in the code
			return (text.IndexOf(this.openTag) != -1);
		}
		
		public void AddOutput(string id, string output)
		{
			this.outputs[id] = output;
		}

		public void AssembleCode()
		{
			string assemblyCode = "";
			assemblyCode += "using System;\r\n";//using lines?
			assemblyCode += "using System.Collections;\r\n";
			assemblyCode += "using Conversive.Verbot4;\r\n";
			foreach(CodeModule cm in this.codeModules)
			{
				assemblyCode += "\r\n";
				//convert cm to class
				assemblyCode += this.codeModule2Class(cm);
			}//foreach(CodeModule cm in this.codeModules)

			assemblyCode += this.conditions2Class();
			assemblyCode += this.outputs2Class();

			this.code = assemblyCode;
		}

		public bool Compile()//true if successful
		{
			bool success;
			this.AssembleCode();//convert the data structures to this.code

			//compile class into assembly
			CSharpCodeProvider codeProvider = new CSharpCodeProvider();
			ICodeCompiler compiler = codeProvider.CreateCompiler();
			
			CompilerParameters parameters = new CompilerParameters();
			parameters.GenerateInMemory = true;
			parameters.GenerateExecutable = false;
			//this should come from some Using collection
			parameters.ReferencedAssemblies.Add("system.dll");
			//the following line doesn't work in web mode
			//parameters.ReferencedAssemblies.Add(Application.StartupPath + Path.DirectorySeparatorChar + "Verbot4Library.dll");
			//use this instead
			parameters.ReferencedAssemblies.Add(System.Reflection.Assembly.GetExecutingAssembly().Location);

			CompilerResults results = compiler.CompileAssemblyFromSource(parameters, this.code);
			if(!results.Errors.HasErrors)//if no errors
			{
				success = true;
				this.assembly = results.CompiledAssembly;
			}
			else// some errors
			{
				success = false;
				foreach(CompilerError error in results.Errors)
				{
					if(error.IsWarning)
					{
						if(this.OnCompileWarning != null)
							this.OnCompileWarning(error.ErrorText, this.getLineSubstring(this.code, error.Line));
					}
					else
					{
						if(this.OnCompileError!= null)
							this.OnCompileError(error.ErrorText, this.getLineSubstring(this.code, error.Line));
					}				
				}//foreach(CompilerError error in results.Errors)
			}
			return success;
		}//Compile()

		public bool ExecuteCondition(string id)
		{
			return this.ExecuteCondition(id, new Hashtable());
		}//ExecuteCondition(string id)

		public bool ExecuteCondition(string id, Hashtable vars)
		{
			try
			{
				if(this.conditions[id] == null)
				{
					return true;
				}
				else if(this.assembly != null)
				{
					Thread exeThread = new Thread(new ThreadStart(this.runCondition));
					Hashtable job = new Hashtable();
					job["name"] = this.COND_PREFIX + id;
					job["vars"] = new StringTable(vars);
					this.threadJobs[exeThread] = job;
					exeThread.IsBackground = true;
					exeThread.Start();
					exeThread.Join(5000);//join when done or in 5 sec.
					bool result = (bool)this.threadJobs[exeThread];
					this.threadJobs[exeThread] = null;
					return result;
				}
			} 
			catch{}
			return false;//error
		}//ExecuteCondition(string id, Hashtable vars)

		public void runCondition()
		{
			try
			{
				Hashtable job = (Hashtable)this.threadJobs[Thread.CurrentThread];
				if(job != null)
				{
					Type type = this.assembly.GetType("Conditions");
					object[] args = { job["vars"] };
					bool result = (bool)type.InvokeMember((string)job["name"], BindingFlags.InvokeMethod, null, assembly, args);
					this.threadJobs[Thread.CurrentThread] = result;
				}
			}
			catch
			{
				this.threadJobs[Thread.CurrentThread] = false;
			}
		}//runCondition()

		public string ExecuteOutput(string id)
		{
			return this.ExecuteOutput(id, new Hashtable());
		}//ExecuteOutput(string id)

		public string ExecuteOutput(string id, Hashtable vars)
		{
			string output = "";
			TextWriter consoleOut = Console.Out;
			try
			{
				if(this.assembly != null)
				{
					Thread exeThread = new Thread(new ThreadStart(this.runOutput));
					Hashtable job = new Hashtable();
					job["name"] = this.OUTPUT_PREFIX + id;
					job["vars"] = new StringTable(vars);
					this.threadJobs[exeThread] = job;
					exeThread.IsBackground = true;
					exeThread.Start();
					exeThread.Join(5000);//join when done or in 5 sec.
					//copy any vars changes back into the main vars object
					vars.Clear();//we need to do this in case any were deleted
					foreach(string key in ((StringTable)job["vars"]).Keys)
					{
						if(vars[key] == null || vars[key] is string)
							vars[key] = ((StringTable)job["vars"])[key];
					}
					output = (string)this.threadJobs[exeThread];
					this.threadJobs[exeThread] = null;
				}
			} 
			catch{}
			finally
			{
				Console.SetOut(consoleOut);
			}
			return output;
		}//ExecuteOutput(string id, Hashtable vars)

		public void runOutput()
		{
			try
			{
				Hashtable job = (Hashtable)this.threadJobs[Thread.CurrentThread];
				if(job != null)
				{
					MemoryStream memStream = new MemoryStream(512);
					StreamWriter writer = new System.IO.StreamWriter(memStream);
					Console.SetOut(writer);

					Type type = this.assembly.GetType("Outputs");
					object[] args = { job["vars"] };
					type.InvokeMember((string)job["name"], BindingFlags.InvokeMethod, null, assembly, args);

					writer.Flush();
					byte[] byteArray = memStream.ToArray();
					char[] charArray = Encoding.UTF8.GetChars(byteArray);
					this.threadJobs[Thread.CurrentThread] = new string(charArray);
				}
			}
			catch
			{
				this.threadJobs[Thread.CurrentThread] = "";
			}
		}//runCondition()

		public string ShowCodeModuleClassCode(CodeModule cm)
		{
			return this.codeModule2Class(cm);
		}

		public bool ConditionExists(string id)
		{
			return this.conditions[id] != null;
		}

		public bool OutputExists(string id)
		{
			return this.outputs[id] != null;
		}

		private string codeModule2Class(CodeModule cm)
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("public class ");
			sb.Append(cm.Name);
			sb.Append(" {\r\n");//open class
			foreach(Function f in cm.Functions)
			{
				sb.Append("public ");
				sb.Append("static ");//all methods are static
				sb.Append(f.ReturnType);
				sb.Append(" ");
				sb.Append(f.Name);
				sb.Append("(");
				sb.Append(f.Parameters);
				sb.Append(") {\r\n");//open method
				sb.Append(f.Code);
				sb.Append("}\r\n");//close method
			}
			sb.Append("}");//close class
			return sb.ToString();
		}//CodeModule2Class()

		private string conditions2Class()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("public class Conditions");
			sb.Append(" {\r\n");//open class
			foreach(string id in this.conditions.Keys)
			{
				sb.Append("public static bool ");
				sb.Append(this.COND_PREFIX);//conditional prefix
				sb.Append(id);
				sb.Append("(StringTable vars) {\r\n");//open method
				sb.Append("return ");
				sb.Append((string)this.conditions[id]);
				sb.Append(";}\r\n");//close method
			}
			sb.Append("}");//close class
			return sb.ToString();
		}//conditions2Class()

		private string outputs2Class()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("public class Outputs");
			sb.Append(" {\r\n");//open class
			foreach(string id in this.outputs.Keys)
			{
				sb.Append("public static void ");
				sb.Append(this.OUTPUT_PREFIX);//output prefix
				sb.Append(id);
				sb.Append("(StringTable vars) {\r\n");//open method
				sb.Append(this.outputToCode((string)this.outputs[id]));
				sb.Append("}\r\n");//close method
			}
			sb.Append("}");//close class
			return sb.ToString();
		}//outputs2Class()

		private string outputToCode(string text)
		{
			if(text == null)
				return "";

			StringBuilder sb = new StringBuilder();
			string uncode = "";
			string code = "";
			//break the text apart into text and code sections
			//test needs to be written to Console.Out
			//and code needs to be written as is
			while(text != "")
			{
				int openTagPos = text.IndexOf(this.openTag);
				while(openTagPos != -1 && TextToolbox.IsEscaped(text, openTagPos))
				{
					text = text.Remove(openTagPos-1, 1);
					openTagPos = text.IndexOf(this.openTag, openTagPos);
				}

				if(openTagPos != 0)//we have uncode to process
				{
					if(openTagPos == -1)//it's all uncode
					{
						uncode = text;
						text = "";
					}
					else//it's uncode + code
					{
						uncode = text.Substring(0, openTagPos);
						text = text.Substring(openTagPos);
					}

					sb.Append("Console.Write(\"");
					sb.Append(this.escape(uncode));
					sb.Append("\");\r\n");
				}
				else//we have code to process (open is at the beginning)
				{
					int closeTagPos = text.IndexOf(this.closeTag);
					while(closeTagPos != -1 && TextToolbox.IsEscaped(text, closeTagPos))
					{
						text = text.Remove(closeTagPos-1, 1);
						closeTagPos = text.IndexOf(this.closeTag, closeTagPos);
					}
					if(closeTagPos == -1)
						closeTagPos = text.Length;
					code = text.Substring(this.openTag.Length, closeTagPos - this.openTag.Length).Trim();
					if(code != "")
						sb.Append(code);
					if(closeTagPos + this.closeTag.Length < text.Length)
						text = text.Substring(closeTagPos + this.closeTag.Length);
					else
						text = "";
				}
			}//while(text != "")

			return sb.ToString();
		}//outputToCode(string text)

		private string escape(string text)
		{
			//replaces \ with \\, and " with \"
			text = text.Replace("\\", "\\\\");//backslash
			text = text.Replace("\"", "\\\"");//quote
			text = text.Replace("\r", "\\r");//return
			text = text.Replace("\n", "\\n");//new line
			text = text.Replace("\t", "\\t");//tab
			text = text.Replace("\f", "\\f");//line feed
			return text;
		}

		private string getLineSubstring(string text, int lineNum)
		{
			string[] splits = text.Split('\n');
			if(splits.Length >= lineNum)
				return splits[lineNum-1].Trim();
			else
				return "";
		}//getLine(string text, int lineNum)

	}//public class CSharpToolbox

	public class StringTable : Hashtable
	{
		public StringTable(Hashtable table) : base(table)
		{
		}

		public string this [string key]
		{
			get
			{
				key = key.ToLower();
				if(base[key] == null)
					return (string)base[key];//return null string
				else
					return base[key].ToString();
			}
			set
			{
				key = key.ToLower();
				base[key] = value;
			}
		}
	}//class StringTable : Hashtable
}//namespace Verbot4Library

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

MattsterP
Web Developer
United States United States
Matt Palmerlee is a Software Engineer that has been working in the Microsoft.NET environment developing C# WebServices, Windows Applications, Web Applications, and Windows Services since 2003.

| Advertise | Privacy | Mobile
Web02 | 2.8.140814.1 | Last Updated 21 Jun 2007
Article Copyright 2007 by MattsterP
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid