Single Fighter Single Fighter - 10 months ago 68
C# Question

How to use "using static" directive for dynamically generated code?

I want to let the users input mathematics expression in terms of

as natural as possible. For example, instead of typing
, I prefer to use just

The following code fails when
, for example, is defined by the user.

using Microsoft.CodeAnalysis.CSharp.Scripting;
using System;
using System.Numerics;
using static System.Console;
using static System.Numerics.Complex;

namespace MathEvaluator
public class Globals
public Complex x;
public Complex y;

class Program

async static void JobAsync(Microsoft.CodeAnalysis.Scripting.Script<Complex> script)
Complex x = new Complex(1, 0);
Complex y = new Complex(0, 1);
var result = await script.RunAsync(new Globals { x = x, y = y });
WriteLine($"{x} * {y} = {result.ReturnValue}\n");
catch (Exception e)

static void Main(string[] args)

Console.Write("Define your expression in x and y: ");
string expression = Console.ReadLine(); //user input

var script = CSharpScript.Create<Complex>(expression, globalsType: typeof(Globals));




How to use
using static
directive for dynamically generated code?


You can supply script options to the Create function that define the references and imports that should be set for your script:

var scriptOptions = ScriptOptions.Default

var script = CSharpScript.Create<Complex>(expression, options: scriptOptions, globalsType: typeof(Globals));

That way, you can use Sin(x) in the input:

Define your expression in x and y: Sin(x)
(1, 0) * (0, 1) = (0,841470984807897, 0)

However, when dealing with user input, you should consider writing your own parser. This allows you on one hand to define your own “aliases” for functions (e.g. a lower case sin) or even a more lenient syntax; on the other hand, it also adds more security because right now, nothing prevents me from doing this:

Define your expression in x and y: System.Console.WriteLine("I hacked this calculator!")
I hacked this calculator!
(1, 0) * (0, 1) = (0, 0)

I created a quick (and dirty) parser using Roslyn’s syntax tree parsing. Obviously this is rather limited (e.g. since it requires all return values of subexpressions to be Complex), but this could give you an idea of how this could work:

void Main()
    string input = "y + 3 * Sin(x)";
    var options = CSharpParseOptions.Default.WithKind(Microsoft.CodeAnalysis.SourceCodeKind.Script);
    var expression = CSharpSyntaxTree.ParseText(input, options).GetRoot().DescendantNodes().OfType<ExpressionStatementSyntax>().FirstOrDefault()?.Expression;


Complex EvaluateExpression(ExpressionSyntax expr)
    if (expr is BinaryExpressionSyntax)
        var binExpr = (BinaryExpressionSyntax)expr;
        var left = EvaluateExpression(binExpr.Left);
        var right = EvaluateExpression(binExpr.Right);

        switch (binExpr.OperatorToken.ValueText)
            case "+":
                return left + right;
            case "-":
                return left - right;
            case "*":
                return left * right;
            case "/":
                return left / right;
                throw new NotSupportedException(binExpr.OperatorToken.ValueText);
    else if (expr is IdentifierNameSyntax)
        return GetValue(((IdentifierNameSyntax)expr).Identifier.ValueText);
    else if (expr is LiteralExpressionSyntax)
        var value = ((LiteralExpressionSyntax)expr).Token.Value;
        return float.Parse(value.ToString());
    else if (expr is InvocationExpressionSyntax)
        var invocExpr = (InvocationExpressionSyntax)expr;
        var args = invocExpr.ArgumentList.Arguments.Select(arg => EvaluateExpression(arg.Expression)).ToArray();
        return Call(((IdentifierNameSyntax)invocExpr.Expression).Identifier.ValueText, args);
        throw new NotSupportedException(expr.GetType().Name);

Complex Call(string identifier, Complex[] args)
    switch (identifier.ToLower())
        case "sin":
            return Complex.Sin(args[0]);
            throw new NotImplementedException(identifier);

Complex GetValue(string identifier)
    switch (identifier)
        case "x":
            return new Complex(1, 0);
        case "y":
            return new Complex(0, 1);
            throw new ArgumentException("Identifier not found", nameof(identifier));