2016-07-11 7 views
1

xyの数学的表現を可能な限り自然に入力させたいと考えています。たとえば、Complex.Sin(x)と入力する代わりに、Sin(x)を使用することをお勧めします。動的に生成されたコードに "using static"ディレクティブを使用するには?

たとえば、Sin(x)がユーザーによって定義されている場合、次のコードは失敗します。

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); 

     } 
    } 
} 

質問

どのように動的に生成されたコードのためのusing staticディレクティブを使用するには?

+0

はたぶん、あなたは、ユーザーが入力するための独自のパーサを書く必要がありますがとにかく、そのようにすれば、関数を他のものに簡単にマップすることができます。 – poke

+0

'ScriptOptions.Default.WithImports'。また、 'await'を扱うための良い慣行を見てみるといいでしょう。そうしないと、驚くべきことが起こるかもしれません(例えば'非同期void'は一般的に悪い考えです)。 – Luaan

+0

@Luaan:ありがとう。私はC#の非同期プログラミングに精通していません。あなたがそれを改善できると思うなら、それを答えとして投稿してください。私は本当にそれを感謝し、もちろん私はそれを投票するでしょう。 :-) –

答えて

3

あなたのスクリプトのために設定する必要があります参照して輸入を定義Create機能にスクリプトオプションを供給することができます:

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

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

そのように、あなたが入力中Sin(x)を使用することができます

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

ただし、ユーザー入力を処理する場合は、y私たち自身のパーサー。これにより、一方で関数(例えば、小文字のsin)のための独自の "エイリアス"を定義したり、より寛大な構文を定義することができます。

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

私はロスリンの構文木解析を使用してクイック(汚い)パーサを作成:今、何がこれをやってから私を妨げないため、他の一方で、それはまた、より多くのセキュリティを追加します。明らかにこれはかなり限定されている(例えば、それはComplexをする部分式のすべての戻り値を必要とするため)が、これはあなたに、この仕事ができる方法のアイデア与えることができる:

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)); 
    } 
} 
関連する問題