Scripting

Published November 21, 2007
Scripting with .NET is unbelievably easy. [grin]

I wanted to add scripting support to Brass, and have added it using .NET's excellent powers of reflection and its System.CodeDom.Compiler namespace.

The first thing I need to do is find out which language the source script is written in. I use the extension to check for this.

string ScriptFile = ...; // Name of main script file to compile.CodeDomProvider Provider = null;// Get the extension (eg "cs")string Extension = Path.GetExtension(ScriptFile).ToLowerInvariant();if (Extension.Length > 0 && Extension[0] == '.') Extension = Extension.Substring(1);// Hunt through all available compilers and dig out one with a matching extension.foreach (CompilerInfo Info in CodeDomProvider.GetAllCompilerInfo()) {	if (Info.IsCodeDomProviderTypeValid) {		CodeDomProvider TestProvider = Info.CreateProvider();		if (TestProvider.FileExtension.ToLowerInvariant() == Extension) {			Provider = TestProvider;			break;		}	}}if (Provider == null) throw new CompilerExpection(source, "Script language not supported.");

Now that we have a compiler, we just set some settings, add some references, then compile the source files:

string[] ScriptFiles = ...; // Array of source file name(s) to compile.// Compiler settings:CompilerParameters Parameters = new CompilerParameters();Parameters.GenerateExecutable = false; // Class lib, not .exeParameters.GenerateInMemory = true;// Add references:Parameters.ReferencedAssemblies.Add("System.dll");Parameters.ReferencedAssemblies.Add("Brass.exe");// Compile!CompilerResults Results = Provider.CompileAssemblyFromFile(Parameters, ScriptFiles);


And that's it! In my case I now pass any errors back up to the assembler, and exit if there were any errors:

// Errors?foreach (CompilerError Error in Results.Errors) {	Compiler.NotificationEventArgs Notification = new Compiler.NotificationEventArgs(compiler, Error.ErrorText, Error.FileName, Error.Line);	if (Error.IsWarning) {		compiler.OnWarningRaised(Notification);	} else {		compiler.OnErrorRaised(Notification);	}}// Do nothing if there were errors.if (Results.Errors.HasErrors) return;


Now the task is passed on to reflection; I go through the compiled assembly, hunt down methods and wrap them up for use as native Brass functions and/or directives.

// Grab the public classes from the script.foreach (Type T in Results.CompiledAssembly.GetExportedTypes()) {    // ...}



I've used this technique in the release of my PAL demo; a C# script file is used to encode an image to the 18x304 resolution and format required by the routine.

Previous Entry Brass 3 and software PAL
Next Entry Site update
0 likes 3 comments

Comments

Comments are shown oldest first so the discussion reads top to bottom.
hanoj
Nice article, I think yours is the first example I've seen that actually compiles more than one file at a time. I'm trying to do something similar but I'm getting hung up on the CompileAssemblyFromFile function...it doesn't let me add an array, only a string. Using the CSharpCodeProvider().

Any ideas?
November 27, 2007 01:12 AM
hanoj
Quote:
Original post by hanoj
Nice article, I think yours is the first example I've seen that actually compiles more than one file at a time. I'm trying to do something similar but I'm getting hung up on the CompileAssemblyFromFile function...it doesn't let me add an array, only a string. Using the CSharpCodeProvider().

Any ideas?


NM, did some more research and found that the proper function for me to call for .Net 3.0 is CompileAssemblyFromFileBatch()

Dunno if its late and I'm tired or what, but I swear the 3.0 documentation says CompileAssemblyFromFile() accepts strings.
November 27, 2007 01:21 AM
benryves
Thanks, and glad you got it working. [smile]
November 27, 2007 05:41 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!