我認爲使用操作符重載來獲取語法樹並不是最好的方法。可能最好遍歷語法樹並從中提取所需的信息。可悲的是,C#lambda表達式的AST與IronPython AST不兼容。所以我建立了一個轉換程序來將IronPython AST轉換爲Linq AST。
static void Main(string[] args)
{
var a = true;
var b = true;
var c = true;
Expression<Func<bool>> csAst =() => a && b || c;
var csexpr = csAst.Body;
Console.WriteLine(csexpr.ToString());
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
scope.SetVariable("a", a);
scope.SetVariable("b", b);
scope.SetVariable("c", c);
string code = "a and b or c";
var pyexpr = GetLinqExpressionFromPyExpression(code, scope);
Console.WriteLine(pyexpr.ToString());
}
輸出是:
((value(Parse.Program+<>c__DisplayClass0).a AndAlso value(Parse.Program+<>c__DisplayClass0).b) OrElse value(Parse.Program+<>c__DisplayClass0).c)
((a AndAlso b) OrElse c)
這裏是(不完全)的轉換程序:
static System.Linq.Expressions.Expression GetLinqExpressionFromPyExpression(string pyExpression, ScriptScope scope)
{
ScriptEngine engine = scope.Engine;
ScriptSource source =
engine.CreateScriptSourceFromString(pyExpression, SourceCodeKind.Expression);
SourceUnit sourceUnit = HostingHelpers.GetSourceUnit(source);
LanguageContext context = HostingHelpers.GetLanguageContext(engine);
Parser parser = Parser.CreateParser(
new CompilerContext(sourceUnit, context.GetCompilerOptions(), ThrowingErrorSink.Default),
(PythonOptions)context.Options);
PythonAst ast = parser.ParseFile(true);
SuiteStatement suite = (SuiteStatement)ast.Body;
ExpressionStatement statement = (ExpressionStatement)suite.Statements[0];
IronPython.Compiler.Ast.Expression expression = statement.Expression;
return Convert(expression, scope);
}
static readonly Dictionary<PythonOperator, ExpressionType> linqOpFromPyOp = new Dictionary<PythonOperator, ExpressionType>{
{ PythonOperator.Not, System.Linq.Expressions.ExpressionType.Not },
{ PythonOperator.Pos, System.Linq.Expressions.ExpressionType.UnaryPlus },
{ PythonOperator.Invert, System.Linq.Expressions.ExpressionType.OnesComplement },
{ PythonOperator.Negate, System.Linq.Expressions.ExpressionType.NegateChecked },
{ PythonOperator.Add, System.Linq.Expressions.ExpressionType.AddChecked },
{ PythonOperator.Subtract, System.Linq.Expressions.ExpressionType.SubtractChecked },
{ PythonOperator.Multiply, System.Linq.Expressions.ExpressionType.MultiplyChecked },
{ PythonOperator.Divide, System.Linq.Expressions.ExpressionType.Divide },
{ PythonOperator.TrueDivide, System.Linq.Expressions.ExpressionType.Divide },
{ PythonOperator.Mod, System.Linq.Expressions.ExpressionType.Modulo },
{ PythonOperator.BitwiseAnd, System.Linq.Expressions.ExpressionType.And },
{ PythonOperator.BitwiseOr, System.Linq.Expressions.ExpressionType.Or },
{ PythonOperator.ExclusiveOr, System.Linq.Expressions.ExpressionType.ExclusiveOr },
{ PythonOperator.LeftShift, System.Linq.Expressions.ExpressionType.LeftShift },
{ PythonOperator.RightShift, System.Linq.Expressions.ExpressionType.RightShift },
{ PythonOperator.Power, System.Linq.Expressions.ExpressionType.Power },
//{ PythonOperator.FloorDivide, System.Linq.Expressions.ExpressionType.Divide }, // TODO
{ PythonOperator.LessThan, System.Linq.Expressions.ExpressionType.LessThan },
{ PythonOperator.LessThanOrEqual, System.Linq.Expressions.ExpressionType.LessThanOrEqual },
{ PythonOperator.GreaterThan, System.Linq.Expressions.ExpressionType.GreaterThan },
{ PythonOperator.GreaterThanOrEqual, System.Linq.Expressions.ExpressionType.GreaterThanOrEqual },
{ PythonOperator.Equal, System.Linq.Expressions.ExpressionType.Equal },
{ PythonOperator.NotEqual, System.Linq.Expressions.ExpressionType.NotEqual },
//{ PythonOperator.In, System.Linq.Expressions.ExpressionType. }, // TODO
//{ PythonOperator.NotIn, System.Linq.Expressions.ExpressionType. }, // TODO
//{ PythonOperator.IsNot, System.Linq.Expressions.ExpressionType.TypeIs }, // TODO
{ PythonOperator.Is, System.Linq.Expressions.ExpressionType.TypeIs },
};
static System.Linq.Expressions.Expression Convert(IronPython.Compiler.Ast.Expression node, ScriptScope scope)
{
switch (node.NodeName)
{
case "AndExpression":
{
var _node = (IronPython.Compiler.Ast.AndExpression)node;
return System.Linq.Expressions.BinaryExpression.AndAlso(
Convert(_node.Left, scope),
Convert(_node.Right, scope));
}
case "BinaryExpression":
{
var _node = (IronPython.Compiler.Ast.BinaryExpression)node;
// TODO: do conversion if left and right have different types
return System.Linq.Expressions.BinaryExpression.MakeBinary(
linqOpFromPyOp[_node.Operator],
Convert(_node.Left, scope),
Convert(_node.Right, scope));
}
case "OrExpression":
{
var _node = (IronPython.Compiler.Ast.OrExpression)node;
return System.Linq.Expressions.BinaryExpression.OrElse(
Convert(_node.Left, scope),
Convert(_node.Right, scope));
}
case "NameExpression":
{
var _node = (IronPython.Compiler.Ast.NameExpression)node;
return System.Linq.Expressions.Expression.Parameter(
scope.GetVariable(_node.Name).GetType(),
_node.Name);
}
// TODO: Add further Python Expression types
default:
throw new ArgumentTypeException("unhandled NodeType '" + node.NodeName + "'");
}
}
internal class ThrowingErrorSink : ErrorSink
{
public static new readonly ThrowingErrorSink/*!*/ Default = new ThrowingErrorSink();
private ThrowingErrorSink() { }
public override void Add(SourceUnit sourceUnit, string message, SourceSpan span, int errorCode, Severity severity)
{
if (severity == Severity.Warning)
{
PythonOps.SyntaxWarning(message, sourceUnit, span, errorCode);
}
else
{
throw PythonOps.SyntaxError(message, sourceUnit, span, errorCode);
}
}
}
的問題,與和|是它們比比較運算符(==,<, >,...)少綁定,所以像「a == b | b == c」這樣的東西只能用於括號,這看起來不自然。寫「不」,也很奇怪,但必須使用&和|和/或。 – Rauhotz
我知道,這就是爲什麼我強調,你打算達到的確切行爲無法實現,因爲短路操作不能超載 –