c# - How to use "using static" directive for dynamically generated code? -
i want let users input mathematics expression in terms of x
, y
natural possible. example, instead of typing complex.sin(x)
, prefer use sin(x)
.
the following code fails when sin(x)
, example, defined 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); 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 expression in x , y: "); string expression = console.readline(); //user input var script = csharpscript.create<complex>(expression, globalstype: typeof(globals)); script.compile(); jobasync(script); } } }
question
how use
using static
directive dynamically generated code?
you can supply script options create
function define references , imports should set script:
var scriptoptions = scriptoptions.default .withreferences("system.numerics") .withimports("system.numerics.complex"); var script = csharpscript.create<complex>(expression, options: scriptoptions, globalstype: typeof(globals));
that way, can use sin(x)
in input:
define expression in x , y: sin(x) (1, 0) * (0, 1) = (0,841470984807897, 0)
however, when dealing user input, should consider writing own parser. allows on 1 hand define own “aliases” functions (e.g. lower case sin
) or more lenient syntax; on other hand, adds more security because right now, nothing prevents me doing this:
define expression in x , y: system.console.writeline("i hacked calculator!") hacked calculator! (1, 0) * (0, 1) = (0, 0)
i created quick (and dirty) parser using roslyn’s syntax tree parsing. rather limited (e.g. since requires return values of subexpressions complex
), give idea of how 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; console.writeline(evaluateexpression(expression)); } complex evaluateexpression(expressionsyntax expr) { if (expr 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 identifiernamesyntax) { return getvalue(((identifiernamesyntax)expr).identifier.valuetext); } else if (expr literalexpressionsyntax) { var value = ((literalexpressionsyntax)expr).token.value; return float.parse(value.tostring()); } else if (expr 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)); } }
Comments
Post a Comment