# An evaluator for simple script evaluation

**Date:** 2016-05-01  
**Author:** Kees C. Bakker  
**Categories:** .NET / C#, .NET Framework / C#, KeesTalksTech Utility Pack, Projects  
**Original:** https://keestalkstech.com/an-evaluator-for-simple-script-evaluation/

![An evaluator for simple script evaluation](https://keestalkstech.com/wp-content/uploads/2016/05/annanas.jpg)

---

In a previous blog I explored [how to create a dynamic compiler](/2016/05/how-to-add-dynamic-compilation-to-your-projects/). In this blog I'll explore how to create an evaluator that aids in the compilation of classes. It will take care of the ceremony needed to wrap the code into a class, compile it and retrieve the result.

## The Evaluator class

The basic idea is to feed the evaluator the code and to execute it by calling `Run`. I've added some comments to the class to explain how it works.

```cs
using KeesTalksTech.Utilities.Compilation;
using System;
using System.Collections.Generic;

/// <summary>
/// The evaluator aids in the compilation of classes. It will take care 
/// of the ceremony needed to wrap the code into a class, compile it and retrieve the result.
/// </summary>
public class Evaluator : IEvaluator
{
	private readonly ICompiler _compiler;
	private readonly Type _producerType = typeof(IProducer);

	/// <summary>
	/// Initializes a new instance of the <see cref="Evaluator"/> class.
	/// </summary>
	/// <param name="compiler">The compiler.</param>
	public Evaluator(ICompiler compiler) : this(compiler, null)
	{
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="Evaluator"/> class. This constructor can be 
	/// used by classes that inherit the compiler to change the base type of the compiled 
	/// producer class.
	/// </summary>
	/// <param name="compiler">The compiler.</param>
	/// <param name="producerType">Type of the producer.</param>
	protected Evaluator(ICompiler compiler, Type producerType = null)
	{
		if (compiler == null)
		{
			throw new ArgumentNullException(nameof(compiler));
		}

		_compiler = compiler;

		if (producerType != null && this._producerType != producerType)
		{
			if (!typeof(IProducer).IsAssignableFrom(producerType))
			{
				throw new NotSupportedException("The baseType parameter needs to be an implementation of IProducer.");
			}

			this._producerType = producerType;
			AssemblyLocations.Add(producerType.Assembly.Location);
		}

		Usings.Add("System");
		AssemblyLocations.Add(typeof(object).Assembly.Location);
		AssemblyLocations.Add(typeof(IProducer).Assembly.Location);
	}

	/// <summary>
	/// Some definitions might live in a different assembly that
	/// needs to be referenced in order to compile the DLL.
	/// </summary>
	public List<string> AssemblyLocations { get; } = new List<string>();

	/// <summary>
	/// Adds usings to the code - this makes it easier to create 
	/// scripts because objects can be used by their name instead
	/// of their full name.
	/// </summary>
	/// <value>
	/// The usings.
	/// </value>
	public List<string> Usings { get; } = new List<string>();

	/// <summary>
	/// Generates the class en compiles it into a producer.
	/// </summary>
	/// <param name="code">The code.</param>
	/// <returns>The producer.</returns>
	public IProducer CreateProducer(string code)
	{
		var args = new CompilerInstructions();
		args.ClassName = "_" + Guid.NewGuid().ToString("N");
		args.Code = @"<<USINGS>>

public class <<CLASS_NAME>>: <<BASE_TYPE>>
{
	public <<MODIFIER>> object Run()
	{
		<<CODE>>;
	
		return null;
	}
}";
		args.Code = args.Code.Replace("<<CLASS_NAME>>", args.ClassName);
		args.Code = args.Code.Replace("<<BASE_TYPE>>", FixFullName(_producerType));
		args.Code = args.Code.Replace("<<CODE>>", code);
		args.AssemblyLocations.AddRange(this.AssemblyLocations);

		if (Usings.Count > 0)
		{
			string usings = "using " + String.Join(";\nusing ", Usings) + ";";
			args.Code = args.Code.Replace("<<USINGS>>", usings);
		}
		else
		{
			args.Code = args.Code.Replace("<<USINGS>>", "");
		}

		if (_producerType.IsClass)
		{
			args.Code = args.Code.Replace("<<MODIFIER>>", "override");
		}
		else
		{
			args.Code = args.Code.Replace("<<MODIFIER>>", "");
		}

		return _compiler.CompileAndCreateObject<IProducer>(args);
	}

	/// <summary>
	/// Runs the specified code.
	/// </summary>
	/// <param name="code">The code.</param>
	/// <returns>The result.</returns>
	public object Run(string code)
	{
		var producer = CreateProducer(code);
		return producer.Run();
	}

	/// <summary>
	/// Fixes the full name.
	/// </summary>
	/// <param name="type">The type.</param>
	/// <returns>The full name.</returns>
	private string FixFullName(Type type)
	{
		return type.FullName.Replace("+", ".");
	}
}
```

A script doesn't have to produce anything, that's why the generated class returns null by default. If the specified script does not have to have a return. The script also adds an extra ';' to the script.

## Hello world script

So let's create a small script that prints Hello World! 10 times:

```cs
var compiler = new CodeDomCompiler();
var evaluator = new Evaluator(compiler);

for (var i = 1; i <= 10; i++)
{
	var script = "Console.WriteLine(\"" + i.ToString("00") + ": Hello world!\");";
	evaluator.Run(script);
}
```

The following example shows how to return a string and print it:

```cs
var compiler = new CodeDomCompiler();
var evaluator = new Evaluator(compiler);

for (var i = 1; i <= 10; i++)
{
	var script = "return \"" + i.ToString("00") + ": Hello world!\";";
	var result = evaluator.Run<string>(script);
	Console.WriteLine(result);
}
```

## Evaluation with a special class

What if you want to interact with the object that was compiled? That's possible. First create a generic evaluator:

```cs
using KeesTalksTech.Utilities.Compilation;

/// <summary>
/// Evaluator that will generate sripts with a certain base type.
/// </summary>
/// <typeparam name="BaseType">The base type of the classes that will be generated by the evaluator.</typeparam>
/// <seealso cref="KeesTalksTech.Utiltities.Evaluation.Evaluator" />
public class Evaluator<BaseType> : Evaluator where BaseType : class, IProducer
{
	/// <summary>
	/// Initializes a new instance of the <see cref="Evaluator{BaseType}"/> class.
	/// </summary>
	/// <param name="compiler">The compiler.</param>
	public Evaluator(ICompiler compiler) : base(compiler, typeof(BaseType))
	{
	}

	/// <summary>
	/// Creates the producer.
	/// </summary>
	/// <param name="code">The code.</param>
	/// <returns>The producer.</returns>
	public new BaseType CreateProducer(string code)
	{
		return (BaseType)base.CreateProducer(code);
	}
}
```

Next create an abstract class that defines the interaction. This class needs to implement `IProducer`:

```cs
public abstract class MyProducer : IProducer
{
	public int Counter { get; set; }

	public string Message { get; set; }

	public abstract object Run();
}
```

Let's see it in action:

```cs
var compiler = new CodeDomCompiler();
var evaluator = new Evaluator<MyProducer>(compiler);

var script = @"
	var s = Counter.ToString(""0000"");
	s += "" "" + Message;
	Console.WriteLine(s);
";

var obj = evaluator.CreateProducer(script);
obj.Message = "Hello World!";
obj.Counter = 1;

for (var i = 0; i <= 10; i++)
{
	obj.Run();
	obj.Counter *= 2;
}
```

So that's it. Happy coding!
