あなたのスクリプトのために設定する必要があります参照して輸入を定義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));
}
}
はたぶん、あなたは、ユーザーが入力するための独自のパーサを書く必要がありますがとにかく、そのようにすれば、関数を他のものに簡単にマップすることができます。 – poke
'ScriptOptions.Default.WithImports'。また、 'await'を扱うための良い慣行を見てみるといいでしょう。そうしないと、驚くべきことが起こるかもしれません(例えば'非同期void'は一般的に悪い考えです)。 – Luaan
@Luaan:ありがとう。私はC#の非同期プログラミングに精通していません。あなたがそれを改善できると思うなら、それを答えとして投稿してください。私は本当にそれを感謝し、もちろん私はそれを投票するでしょう。 :-) –