C# Interactive Interpreter
Last Updated :
25 Apr, 2025
This article is designed to showcase the step by step implementation of Interactive C# Interpreter Development by virtue of .NET framework codeDOM API’s which enables us to dynamically evaluate C# code expressions and statements for testing mini code fragments or scripts more or less like to Python Interpreter. However, the visual studio is capable enough to do so, then the question occurs why we need it? The visual studio IDE compiler typically compiles the whole application classes every time rather than a chunk of code segments if required. This process is, therefore, relatively cumbersome, time-consuming, and inflicts an unduly extra overhead on the file system.
In this regard, there are multiple classes will be carved-out with varied functionality. The aspirant is supposed to have a proficiency in .NET technology as this project is a console-based application where the interpreter gives us a command-line manner impression to operate the commands. Hence, the following C# code snippet is the entry point of the application wherefrom the actual execution will be begun by incorporating the calls of all other subsidiary essential classes. In the entry point class called Program, we found the banner to be displayed along with the custom exception handling functionality.
program.cs
csharp
// Entry Point
class Program {
static void Main(string[] args)
{
// verbose entry checking
if (args.Length > 0 && args[0].Equals("--verbose",
StringComparison.InvariantCultureIgnoreCase)) {
Interactive.Context.VerboseTrace = true;
}
Trace.Listeners.Add(new ConsoleTraceListener());
// Method call for welcome message
Program.WriteWelcomeMessage();
// Display #:-> on the shell
while (Interactive.Context.Continue) {
Console.Write("#:->");
string text = Console.ReadLine();
if (text == null) {
return;
}
// Start the interactive shell by the function call
try {
string text2 = Interactive.Interpret(text);
if (text2 != null) {
Console.WriteLine(text2);
}
}
catch (TargetInvocationException ex) {
Program.WriteExceptionMessage(ex.InnerException);
}
catch (Exception ex2) {
Program.WriteExceptionMessage(ex2);
}
}
}
// Starts the Interactive shell, and display the welcome message
private static void WriteWelcomeMessage()
{
Version version = Assembly.GetExecutingAssembly().GetName().Version;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("-----------------------------------------");
Console.WriteLine("\tC# Interactive Interpreter\n");
Console.WriteLine("-----------------------------------------");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("help -- Help");
Console.WriteLine("quit -- Quit");
Console.WriteLine("clear-- Clear Screen");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine();
}
// Exception handling section
private static void WriteExceptionMessage(Exception ex)
{
Console.WriteLine("Exception of type '"
+ ex.GetType().Name + "' was thrown: "
+ ex.Message);
if (Interactive.Context.VerboseTrace) {
Console.WriteLine("StackTrace:");
Console.WriteLine(ex.StackTrace);
if (ex.InnerException != null) {
Console.WriteLine("Inner Exception:");
Console.WriteLine(ex.InnerException.Message);
Console.WriteLine(ex.InnerException.StackTrace);
}
}
}
}
outString.cs The outString class held responsible for the initialization of essentials variable which will be conducive to printing the result on the shell.
csharp
public class outString {
// Properties declaration
public string Value
{
get;
private set;
}
public outString(string value)
{
this.Value = value;
}
public override string ToString()
{
return this.Value;
}
}
Interactive.cs The Interactive class encapsulates all the necessary code in importing APIs, Invoking the execution session, Regular expression handling and dealt with the compiler and interpreter API and the method by invoking CodeDOM class. Overall, this is the sole class to interpret the statement, just like an engine.
csharp
public static class Interactive {
private static readonly CodeDomProvider Compiler;
public static exeContext Context;
static Interactive()
{
// C# API calling
Interactive.Compiler = CodeDomProvider.CreateProvider("C#");
Interactive.Context = new exeContext();
}
// empty the context
public static void Reset()
{
Interactive.Context = new exeContext();
}
// Invoke the interpreter shell
public static string Interpret(string sourceCode)
{
return sourceCode.CompileCodeSnippet().Invoke();
}
// Compile the bunch input C# code
private static compiledCode CompileCodeSnippet(this string code)
{
if (Interactive.Context.MultiLine) {
exeContext expr_11 = Interactive.Context;
expr_11.MultiLineStatement += code;
code = Interactive.Context.MultiLineStatement;
}
return code.Statement() || code.TypeMember();
}
// Compile the Particular C# statement
private static compiledCode Statement(this string code)
{
return code.ExpressionStatement() || code.UsingStatement() || code.GenericStatement();
}
// Compile the "Using" statements
private static compiledCode UsingStatement(this string code)
{
compiledCode result = null;
if (code.TrimStart(new char[0]).StartsWith("using ")) {
string text = code.TrimEnd(new char[0]);
if (!text.EndsWith(";")) {
text += ";";
}
string usingStatement = text;
string source = Interactive.Program(null, null, null, null, usingStatement);
custStatement statement = new custStatement(code, source.CompileFromSource());
if (!statement.HasErrors) {
Interactive.Context.UsingStatements.Add(text);
result = statement;
}
}
return result;
}
// In case custom statement compilation
private static compiledCode GenericStatement(this string code)
{
compiledCode result = null;
string statement = code + ";";
string source = Interactive.Program(null, statement, null, null, null);
custStatement statement2 = new custStatement(code, source.CompileFromSource());
if (!statement2.HasErrors) {
Interactive.Context.CallStack.Add(code + ";");
result = statement2;
}
else {
if (!Interactive.Context.MultiLine && (statement2.Errors[0].ErrorNumber == "CS1513" || statement2.Errors[0].ErrorNumber == "CS1528")) {
Interactive.Context.MultiLine = true;
exeContext expr_A2 = Interactive.Context;
expr_A2.MultiLineStatement += code;
}
}
return result;
}
// Section to execute "Clear" command
private static compiledCode ExpressionStatement(this string expr)
{
string returnStatement = custProBuilds.ReturnStatement(expr);
custExpression expression = new custExpression(expr, Interactive.Program(null, null, returnStatement, null, null).CompileFromSource());
if (!expression.HasErrors && !expr.Trim().Equals("clear", StringComparison.OrdinalIgnoreCase)) {
string text = "__" + Guid.NewGuid().ToString().Replace("-", "");
Interactive.Context.CallStack.Add(string.Concat(new string[] {
"var ",
text,
" = ",
expr,
";" }));
}
return expression;
}
// Incorporate the "Program" class code
public static string Program(string typeDeclaration = null, string statement = null, string returnStatement = null, string memberDeclaration = null, string usingStatement = null)
{
return custProBuilds.Build(Interactive.Context, typeDeclaration, statement, returnStatement, memberDeclaration, usingStatement);
}
// Incorporate the class type defined members code
private static compiledCode TypeMember(this string source)
{
return source.TypeDeclaration() || source.MemberDeclaration() || source.FieldDeclaration();
}
// Incorporate the member declaration code
private static compiledCode MemberDeclaration(this string code)
{
custMemDecl memberDeclaration = new custMemDecl(code, Interactive.Program(null, null, null, code, null).CompileFromSource());
if (!memberDeclaration.HasErrors) {
Interactive.Context.MemberDeclarations.Add(code);
}
return memberDeclaration;
}
// Incorporate the type declaration code and add them
private static compiledCode TypeDeclaration(this string source)
{
string source2 = Interactive.Program(source, null, null, null, null);
custTypeDecl typeDeclaration = new custTypeDecl(source, source2.CompileFromSource());
if (!typeDeclaration.HasErrors) {
Interactive.Context.TypeDeclarations.Add(source);
}
return typeDeclaration;
}
// Incorporate the class fields code
private static compiledCode FieldDeclaration(this string code)
{
string text = code + ";";
string memberDeclaration = text;
custMemDecl memberDeclaration2 = new custMemDecl(code, Interactive.Program(null, null, null, memberDeclaration, null).CompileFromSource());
if (!memberDeclaration2.HasErrors) {
Interactive.Context.MemberDeclarations.Add(text);
}
return memberDeclaration2;
}
// Gather exception traces
private static string Invoke(this compiledCode compiledCode)
{
if (Interactive.Context.MultiLine && !compiledCode.HasErrors) {
Interactive.Context.MultiLine = false;
Interactive.Context.MultiLineStatement = "";
}
if (!Interactive.Context.MultiLine && compiledCode.HasErrors) {
Interactive.TraceErrorMessage(compiledCode);
}
if (!Interactive.Context.MultiLine && !compiledCode.HasErrors && (compiledCode is custExpression || compiledCode is custStatement)) {
Interactive.Context.MultiLine = false;
Interactive.Context.MultiLineStatement = "";
object result = Interactive.InvokeCompiledResult(compiledCode.Results);
if (compiledCode is custExpression) {
return result.FormatOutput();
}
}
return null;
}
// determine the error number in the code
private static void TraceErrorMessage(compiledCode compiledCode)
{
Trace.TraceError(compiledCode.Errors[0].ErrorText);
if (Interactive.Context.VerboseTrace) {
Trace.TraceError(compiledCode.Errors[0].ErrorNumber);
}
}
// Finally, invoke the concatenated code
private static object InvokeCompiledResult(CompilerResults results)
{
Assembly compiledAssembly = results.CompiledAssembly;
Type type = compiledAssembly.GetType("Wrapper");
object obj = Activator.CreateInstance(type, null);
MethodInfo method = type.GetMethod("Eval");
return method.Invoke(obj, null);
}
// method to compile the whole code by incorporating the library class
private static CompilerResults CompileFromSource(this string source)
{
CompilerParameters compilerParameters = new CompilerParameters{
GenerateExecutable = false,
GenerateInMemory = true
};
compilerParameters.ReferencedAssemblies.Add("System.Core.dll");
compilerParameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
compilerParameters.ReferencedAssemblies.Add("System.Xml.dll");
compilerParameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");
compilerParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
foreach(string current in exeContext.Assemblies)
{
compilerParameters.ReferencedAssemblies.Add(current);
}
return Interactive.Compiler.CompileAssemblyFromSource(compilerParameters, new string[] {
source });
}
}
exeContext.cs The exeContext loads all the type declaration, classes in using statements, assemblies, and call stack into the current context.
csharp
public class exeContext {
// Loads using, assembly, type declaration, and member function in linked list
public static List<string> Assemblies = new List<string>();
public IList<string> CallStack = new List<string>();
public IList<string> TypeDeclarations = new List<string>();
public List<string> MemberDeclarations = new List<string>();
public List<string> UsingStatements = new List<string>();
// properties for single, multiline, verbose, and context handling
public bool MultiLine
{
get;
set;
}
public string MultiLineStatement
{
get;
set;
}
public bool VerboseTrace
{
get;
set;
}
public bool Continue
{
get;
set;
}
public exeContext()
{
this.Continue = true;
this.MultiLineStatement = "";
}
// Load the executed assembly into the current context
public static void LoadAssembly(string name)
{
FileInfo fileInfo = new FileInfo(name);
FileInfo fileInfo2 = new FileInfo(Assembly.GetExecutingAssembly().Location);
if (fileInfo.DirectoryName != fileInfo2.DirectoryName) {
if (fileInfo2.DirectoryName != null) {
if (!File.Exists(Path.Combine(fileInfo2.DirectoryName, fileInfo.Name))) {
fileInfo.CopyTo(Path.Combine(fileInfo2.DirectoryName, fileInfo.Name), true);
}
exeContext.Assemblies.Add(fileInfo.Name);
return;
}
}
else {
exeContext.Assemblies.Add(name);
}
}
}
custProBuild.cs Another important one class in interactive compiles as it takes the code instruction in string mode concatenated them and finally interpreted it in inline verbose mode.
csharp
public class exeContext
{
// Loads using, assembly, type declaration, and member function in linked list
public static List<string> Assemblies = new List<string>();
public IList<string> CallStack = new List<string>();
public IList<string> TypeDeclarations = new List<string>();
public List<string> MemberDeclarations = new List<string>();
public List<string> UsingStatements = new List<string>();
// properties for single, multiline, verbose, and context handling
public bool MultiLine
{
get;
set;
}
public string MultiLineStatement
{
get;
set;
}
public bool VerboseTrace
{
get;
set;
}
public bool Continue
{
get;
set;
}
public exeContext()
{
this.Continue = true;
this.MultiLineStatement = "";
}
// Load the executed assembly into the current context
public static void LoadAssembly(string name)
{
FileInfo fileInfo = new FileInfo(name);
FileInfo fileInfo2 = new FileInfo(Assembly.GetExecutingAssembly().Location);
if (fileInfo.DirectoryName != fileInfo2.DirectoryName)
{
if (fileInfo2.DirectoryName != null)
{
if (!File.Exists(Path.Combine(fileInfo2.DirectoryName, fileInfo.Name)))
{
fileInfo.CopyTo(Path.Combine(fileInfo2.DirectoryName, fileInfo.Name), true);
}
exeContext.Assemblies.Add(fileInfo.Name);
return;
}
}
else
{
exeContext.Assemblies.Add(name);
}
}
}
custOutput.cs The custOutput class displayed the output of the interpreted C# codes by generating an XML file of the calculated resulted and then printed the output in the valid form. This class accepts input in various types including Array, dictionary, XML, enumerable, string and produce the output based on the passed value.
csharp
// Abstract class for an exception related properties declaration
public abstract class compiledCode
{
public string Source
{
get;
set;
}
public CompilerResults Results
{
get;
set;
}
public CompilerErrorCollection Errors
{
get;
set;
}
public bool HasErrors
{
get
{
return this.Errors.HasErrors;
}
}
protected compiledCode(string source, CompilerResults results)
{
this.Source = source;
this.Results = results;
this.Errors = this.Results.Errors;
}
public static compiledCode operator |(compiledCode a, compiledCode b)
{
if (a == null)
{
return b;
}
if (b == null)
{
return a;
}
if (!b.HasErrors)
{
return b;
}
return a;
}
public static bool operator false (compiledCode a)
{
return false;
}
public static bool operator true (compiledCode a)
{
return a != null && !a.HasErrors;
}
}
compileCode.cs
Furthermore, the compileCode class is used to declare some essential properties conducive during the interpretation of code and dealing with the occurrence of unexpected exception.
csharp
// Abstract class for an exception related properties declaration
public abstract class compiledCode {
public string Source
{
get;
set;
}
public CompilerResults Results
{
get;
set;
}
public CompilerErrorCollection Errors
{
get;
set;
}
public bool HasErrors
{
get
{
return this.Errors.HasErrors;
}
}
protected compiledCode(string source, CompilerResults results)
{
this.Source = source;
this.Results = results;
this.Errors = this.Results.Errors;
}
public static compiledCode operator | (compiledCode a, compiledCode b)
{
if (a == null) {
return b;
}
if (b == null) {
return a;
}
if (!b.HasErrors) {
return b;
}
return a;
}
public static bool operator false(compiledCode a)
{
return false;
}
public static bool operator true(compiledCode a)
{
return a != null && !a.HasErrors;
}
}
Other classes Apart from the above important classes, there are other trivial classes code related to custom expression, custom member, and types declared below which are mutually inherited to each other.
csharp
// Custom Expression Handling
public class custExpression : custStatement {
public custExpression(string source, CompilerResults results)
: base(source, results)
{
}
}
// Custom Member Declaration
public class custMemDecl : cusTypeMem {
public custMemDecl(string source, CompilerResults results)
: base(source, results)
{
}
}
// For Custom statement handling
public class custStatement : compiledCode {
public custStatement(string source, CompilerResults results)
: base(source, results)
{
}
}
// For custom type declaration
public class custTypeDecl : cusTypeMem {
public custTypeDecl(string source, CompilerResults compilerResults)
: base(source, compilerResults)
{
}
}
// Custom Members Declaration
public class cusTypeMem : compiledCode {
protected cusTypeMem(string source, CompilerResults results)
: base(source, results)
{
}
}
After incorporating the aforesaid class declaration into a cohesive project solution of visual studio 2015 or later. The following command will be displayed as a resulted after successfully build and compile of the project as follows.
Finally, now we can enjoy the instant execution of C# code instructions (like as we execute the python instruction code), the movement we write the code and press enter as following;
This construct is a simple C# interpreter that enables the command-line compilation of standalone commands. It accepts an inline C# code instruction and later on compiles it on the fly, finally executing the resulting assembly on the spot like Python interpreter.
Similar Reads
Interactive Operating System
Interactive operating systems are computers that accept human inputs. Users give commands or some data to the computers by typing or by gestures. Some examples of interactive systems include MS Word and Spreadsheets, etc. They facilitate interactive behavior. Mac and Windows OS are some examples of
5 min read
What is Command Line Interface (CLI)?
Command Line Interface is used to communicate with a computer program, you can input text into a by typing command lines. In this article, we will understand the workings of the command line interface, features of the command line interface, and more. What is the Command Line Interface?Command line
6 min read
Simple Code Generator
Compiler Design is an important component of compiler construction. It involves many different tasks, such as analyzing the source code and producing an intermediate representation (IR) from it, performing optimizations on the IR to produce a target machine code, and generating external representati
7 min read
Language Processing Activities
Language processing activity bridges the ideas of software designers with actual execution on the computer system. The designer expresses the idea related to the application domain of the software. To implement these ideas, the description of the ideas has to be interpreted as related to the executi
3 min read
What is Python Interpreter
Well if you are new to Python you have come across the word interpreters but before knowing about Python interpreters, we need to what is an interpreter, what it will do, the advantages and disadvantages of the interpreter, and so on. Well in this handy article, we will cover the Importance of Inter
4 min read
Python | Execute and parse Linux commands
Prerequisite: Introduction to Linux Shell and Shell Scripting Linux is one of the most popular operating systems and is a common choice for developers. It is popular because it is open source, it's free and customizable, it is very robust and adaptable. An operating system mainly consists of two par
6 min read
C# Program to Check Whether a Process is Running in User Interactive Mode or Not Using Environment Class
In C#, Environment Class provides information about the current platform and manipulates, the current platform. It is useful for getting and setting various operating system-related information. We can use it in such a way that it retrieves command-line arguments information, exit codes information,
2 min read
C# Program to Demonstrate the Use of GetCommandLineArgs() Method of Environment Class
Environment Class provides information about the current platform and manipulates, the current platform. It is useful for getting and setting various operating system-related information. We can use it in such a way that retrieves command-line arguments information, exit codes information, environme
3 min read
Cmdparse module in Python
The Class which provides a simple framework for writing line-oriented command interpreters is called cmd class. These are often useful for administrative tools, prototypes and test harnesses that will later be wrapped in a more sophisticated interface. The command-line interface can be easily made u
6 min read
Primary and secondary prompt in Python
The ">>>" sign seen on the Python's interactive shell is called a prompt. Python's interactive shell, which appears after firing command "python3" (if we are working on python version 3) on the terminal, has two such prompts. ">>>" - Primary prompt "..." - Secondary prompt When we
2 min read