C# Interactive Interpreter - GeeksforGeeks

21 Jan, 2022
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 Pointclass 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 declarationpublic 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 declarationpublic 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 Handlingpublic class custExpression : custStatement {    public custExpression(string source, CompilerResults results)        : base(source, results)    {    }}// Custom Member Declarationpublic class custMemDecl : cusTypeMem {    public custMemDecl(string source, CompilerResults results)        : base(source, results)    {    }}// For Custom statement handlingpublic class custStatement : compiledCode {    public custStatement(string source, CompilerResults results)        : base(source, results)    {    }}// For custom type declarationpublic class custTypeDecl : cusTypeMem {    public custTypeDecl(string source, CompilerResults compilerResults)        : base(source, compilerResults)    {    }}// Custom Members Declarationpublic 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. 
nidhi_biet
kapoorsagar226
singghakshay
simmytarika5
C#
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
C# Dictionary with examples
Difference between Ref and Out keywords in C#
C# | Delegates
Top 50 C# Interview Questions & Answers
Extension Method in C#
C# | Abstract Classes
C# | String.IndexOf( ) Method | Set - 1
Different ways to sort an array in descending order in C#
C# | Arrays
HashSet in C# with Examples