CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 2 of 2
  1. #1
    Join Date
    Jan 2012
    Posts
    0

    Building Custom Script Interpreter

    Ok, a week ago I started building a script interpreter for my project. It's basically a winsock packet editor with it's own scripting engine to script how you want to handle/respond to packets and when. Basically I'm building an app to make scripted bots for games lol. The original project was made by a friend on another forum which was later handed to me as he lost interest in it.

    Since then I've decided to scrap it's scripting engine that he had written as it was really limited and didn't offer much support for arrays. The idea behind the new scripting engine is to mirror C# as much as possible in both it's function and in syntax and far I think I've done a pretty good job with it.

    Code:
    		public bool seCompile( string[] source ) {
    			Stack<object> blocks = new Stack<object>();
    			object cBlock = null;
    
    			seFunction cCom = null;
    			seFunction cFunc = null;
    			seWhile cWhile = null;
    			seIf cIf = null;
    
    			for( int i = 0; i < source.Count(); i++ ) {
    				string line = source[ i ].Trim();
    
    				if( new Regex( @"^\/\/" ).Match( line ).Value != "" || line == "" )
    					continue;
    				else if( new Regex( @"^\/\*" ).Match( line ).Success ) {
    					if( line.IndexOf( "*/" ) >= 0 )
    						continue;
    					if( cCom != null )
    						continue;
    					cCom = new seFunction();
    					continue;
    				} else if( new Regex( @"\*\/$" ).Match( line ).Success ) {
    					cCom = null;
    					continue;
    				} else if( cCom != null )
    					continue;
    
    				if( new Regex( @"^\}" ).Match( line ).Success ) {
    					if( blocks.Count() == 0 )
    						return Die( source, "Unexpected \"}\"", i );
    					cBlock = blocks.Pop();
    
    					if( cBlock.GetType().Name == "seFunction" ) {
    						( ( seFunction )cBlock ).endLine = i;
    						functionList.Add( ( ( seFunction )cBlock ) );
    						cFunc = null;
    						cBlock = null;
    						continue;
    					} else if( cBlock.GetType().Name == "seWhile" ) {
    						( ( seWhile )cBlock ).endLine = i;
    						whileList.Add( ( ( seWhile )cBlock ) );
    						cWhile = null;
    						cBlock = null;
    						continue;
    					} else if( cBlock.GetType().Name == "seIf" ) {
    						cIf = ( ( seIf )cBlock );
    						if( cIf.elseLine.line > cIf.ifLine ) {
    							cIf.elseLine.endLine = i;
    							ifList.Add( cIf );
    							cIf = null;
    							cBlock = null;
    							continue;
    						}
    						else if( new Regex( @"^\}\s*else\s*\{", RegexOptions.IgnoreCase ).Match( line ).Success ) {
    							cIf.endLine = i;
    							cIf.elseLine.line = i;
    							blocks.Push( cIf );
    							cIf = null;
    							cBlock = null;
    							continue;
    						} 
    						else if( cIf.elseLine.line == cIf.ifLine ) {
    							cIf.endLine = i;
    							cIf.elseLine.endLine = cIf.ifLine;
    							ifList.Add( cIf );
    							cIf = null;
    							cBlock = null;
    							continue;
    						}
    					}
    				}
    
    				if( new Regex( @"^(?<type>[a-zA-Z]\w+(\s*\[\s*\])?)\s+(?<name>[a-zA-Z]\w+)\s*\(\s*([a-zA-Z]\w+(\s*\[\s*\])?\s+[a-zA-Z]\w*,?\s*)*\)\s*\{" ).Match( line ).Success ) {
    					if( cFunc != null || blocks.Count() > 0 )
    						return Die( source, "Unexpected function found", i );
    
    					Match functionMatch = new Regex( @"^(?<type>[a-zA-Z]\w+(\s*\[\s*\])?)\s+(?<name>[a-zA-Z]\w+)\s*\(\s*([a-zA-Z]\w+(\s*\[\s*\])?\s+[a-zA-Z]\w*,?\s*)*\)\s*\{" ).Match( line );
    					GroupCollection functionGroup = functionMatch.Groups;
    
    					cFunc = new seFunction();
    					cFunc.name = functionGroup[ "name" ].Value;
    					cFunc.type = getTypeByString( functionGroup[ "type" ].Value.ToLower() );
    					cFunc.line = i;
    
    					string paramString = line.Substring( new Regex( @"^[a-zA-Z]\w+(\s*\[\s*\])?\s+[a-zA-Z]\w+\s*\(\s*" ).Match( line ).Value.Length, ( line.Length - new Regex( @"[a-zA-Z]\w+(\s*\[\s*\])?\s+[a-zA-Z]\w+\s*\(\s*" ).Match( line ).Value.Length ) );
    					paramString = paramString.Substring( 0, ( paramString.Length - new Regex( @"\)\s*\{$" ).Match( paramString ).Value.Length ) ).Trim();
    					MatchCollection paramMatches = new Regex( @"[a-zA-Z]\w+(\s*\[\s*\])?\s+[a-zA-Z]\w*" ).Matches( paramString );
    
    					foreach( Match match in paramMatches ) {
    						string singleParam = match.Value.Trim();
    						Match paramMatch = new Regex( @"^(?<type>[a-zA-Z]\w+(\s*\[\s*\])?)\s{1,}(?<name>[a-zA-Z]\w*)" ).Match( singleParam );
    						if( !paramMatch.Success )
    							return Die( source, "Syntax error in parameter list", i );
    
    						seVariable param = new seVariable();
    						param.name = paramMatch.Groups[ "name" ].Value;
    						if( getTypeByString( paramMatch.Groups[ "type" ].Value.ToLower() ) == typeof( Nullable ) )
    							return Die( source, "Invalid/Unsupported type definition \"" + paramMatch.Groups["type"].Value + "\" in parameter list", i );
    						param.type = getTypeByString( paramMatch.Groups[ "type" ].Value.ToLower() );
    						cFunc.param.Add( param );
    					}
    
    					blocks.Push( cFunc );
    					cFunc = null;
    					continue;
    				}
    			}
    
    			return true;
    		}
    From the top; textBox1 holds the code, form_load creates a new instance of ScriptEngine and passes a reference to the textBox1 control to the class for future usage( currently not used)

    button1 calls ScriptEngine.seStart( string[] source ) which passes textBox1.Lines as the source.

    void ScriptEngine.seStart( string[] source ) stores a copy of the source in a safe place then calls:
    bool ScriptEngine.seCompile( string[] source );

    If ScriptEngine.seCompile does not return false, everything got parsed correctly and bool seActive gets set to true and a timer object starts calling bool ScriptEngine.Process();
    to execute the script line by line

    The job of seCompile is to check syntax and gather all the information from each code block/line and store them as an instance of their respective classes

    For example, this is how a function would be stored:
    Code:
    	public class seFunction {
    		public List<seVariable> param;
    		public string name;
    		public Type type;
    		public int line;
    		public int endLine;
    
    		public seFunction() {
    			param = new List<seVariable>();
    			name = string.Empty;
    			line = -1;
    			endLine = -1;
    		}
    	}
    When ScriptEngine.Process reaches each line, it will look in List<seFunction> to see if there is a function on that line, and if there is, it will get the seFunction object from the list and run it. If the line is not a function, it will then regex for which type line it is; block(if/while) or statement.
    There is also a completed If and While parser in there which I took out so there is less reading to do.

    Type is returned by Type getTypeByString( string type );

    Before anyone says I should just use an existing scripting language like C#'s codedom or Lua or some crap; I've tried. My issue with them is the lack of return information needed and line tracking as it runs through the script. Line tracking being the biggest issue as I need it to implement a non-blocking sleep() function within the scripting language.

    Also a function called bool waitForPacket(); will return false as long as the packet we're waiting for has not come yet, and I need the scripting engine to re-attempt that call every 2, 5, or 10ms( the winsock hook hands over a packet if there is one every 10ms ) until it's successful and not move from that line until it is.

    Lua does not have a pre-process/parser method so it won't accept multiline functions when using Lua.doLine();

    C#'s CodeDom does not return enough information( or so it seemed ) for me to work with.

    At any rate, any suggestions on improvements ? changes ? etc ?

  2. #2
    Join Date
    Dec 2011
    Posts
    61

    Re: Building Custom Script Interpreter

    You may have a look of ANTLR (http://www.antlr.org)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured