I think using operator overloading to get the syntax tree is not the best way to go. Probably it's better to traverse the syntax tree and extract the information you need from that. Sadly the AST of C# lambda expressions is not compatible with IronPython AST. So I've set up a transformation procedure to convert IronPython AST to 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());
}
Output is:
((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)
And here is the (incomplete) transformation procedure:
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);
}
}
}