2016-07-11 2 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' 작업에 대한 좋은 사례를보고 싶을 수도 있습니다. 그렇지 않으면 예기치 않은 놀라움이 생길 수 있습니다 (예 :'비동기 무효화 '는 일반적으로 나쁜 생각입니다). – 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)); 
    } 
} 
관련 문제