2016-07-11 18 views
1

Ich möchte die Benutzer Mathematik Ausdruck in Bezug auf x und y so natürlich wie möglich eingeben lassen. Anstatt beispielsweise Complex.Sin(x) einzugeben, verwende ich lieber nur Sin(x). Der folgende Code schlägt fehl, wenn Sin(x) zum Beispiel vom Benutzer definiert wird.Wie verwende ich die "using static" -Richtlinie für dynamisch generierten Code?

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); 
      try 
      { 
       var result = await script.RunAsync(new Globals { x = x, y = y }); 
       WriteLine($"{x} * {y} = {result.ReturnValue}\n"); 
      } 
      catch (Exception e) 
      { 
       WriteLine(e.Message); 
      } 
     } 

     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)); 
      script.Compile(); 

      JobAsync(script); 

     } 
    } 
} 

Frage

Wie für dynamisch generierte Code using static Direktive benutzen?

+0

Vielleicht sollten Sie Ihre eigenen Parser für Benutzereingaben schreiben Code sowieso, und auf diese Weise können Sie die Funktionen leicht auf etwas anderes abbilden. – poke

+0

'ScriptOptions.Default.WithImports'. Vielleicht möchten Sie sich auch einige gute Praktiken für die Arbeit mit 'await' ansehen, oder Sie könnten einige böse Überraschungen erleben (z. B.' async void' ist im Allgemeinen eine schlechte Idee). – Luaan

+0

@Luaan: Danke. Ich bin nicht mit der asynchronen Programmierung in C# vertraut. Wenn Sie denken, dass Sie es verbessern können, zögern Sie nicht, es als Antwort zu veröffentlichen. Ich schätze das sehr und natürlich werde ich es abstimmen. :-) –

Antwort

3

Sie können Skriptoptionen auf die Create Funktion, die die Referenzen und die Importe definieren, die für das Skript festgelegt werden soll liefern:

var scriptOptions = ScriptOptions.Default 
    .WithReferences("System.Numerics") 
    .WithImports("System.Numerics.Complex"); 

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

Auf diese Weise können Sie Sin(x) im Eingabe verwenden:

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

Wenn Sie jedoch mit Benutzereingaben arbeiten, sollten Sie das Schreiben von y in Betracht ziehen unser eigener Parser. Auf diese Weise können Sie auf der einen Seite Ihre eigenen "Aliase" für Funktionen definieren (z. B. einen Kleinbuchstaben sin) oder sogar eine mildere Syntax; auf der anderen Seite, es fügt auch mehr Sicherheit, weil gerade jetzt, nichts mir dies zu tun, verhindert:

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

ich eine schnelle (und schmutzig) Parser Roslyn der Syntaxbaum-Analyse erstellt. Offensichtlich ist dies eher begrenzt (zB weil sie alle Rückgabewerte von Teilausdrücke erfordert Complex zu sein), aber dies könnte Ihnen eine Vorstellung davon, wie das funktionieren könnte:

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; 

    Console.WriteLine(EvaluateExpression(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; 
      default: 
       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); 
    } 
    else 
     throw new NotSupportedException(expr.GetType().Name); 
} 

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

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