Here’s a simple C# class that can help debugging Dynamic Language Runtime (v0.9 release) expression trees. It’s nothing fancy; it just dumps the entire tree to an indented string. Typically you’d call its static Dump(node) helper from a watch window, or maybe include it in a debug-only region.
Important note: You can consider this a short-term hack for the v0.9 release of the DLR only. If you prefer to use the latest DLR source from CodePlex, there’s already a public Dump property available on the Expression class. Also, if you’re already using Visual Studio 10 / C# 4.0, the Expression Tree Visualizer will probably work just fine
using System; using Microsoft.Linq.Expressions; using System.Text; namespace Cjc.Calculation.ExpressionEngine { public class ExpressionTreeDump : BlindExpressionTreeVisitor { private int indentSize; private int indent = 0; private StringBuilder sb = new StringBuilder(); public static string Dump( Expression node ) { var dump = new ExpressionTreeDump(); dump.Visit( node ); return dump.Text; } public ExpressionTreeDump() : this( 2 ) { } public ExpressionTreeDump( int indentSize ) { this.indentSize = indentSize; } public string Text { get { return sb.ToString(); } } protected override TResult Visit<T, TResult>( T node, Func<T, TResult> p ) { var e = node as Expression; var text = ( e != null ) ? string.Format( "{{{0}}} {1}", e.NodeType, e ) : node.ToString(); sb.AppendLine( new string( ' ', indent ) + text ); indent += indentSize; var result = base.Visit<T, TResult>( node, p ); indent -= indentSize; return result; } } public class BlindExpressionTreeVisitor : ExpressionTreeVisitor { protected virtual TResult Visit<T, TResult>( T node, Func<T, TResult> p ) { return p( node ); } #region Overrides protected override Expression VisitAssignment( AssignmentExpression node ) { return Visit( node, e => BaseAssignment( e ) );} protected override Expression VisitBinary( BinaryExpression node ) { return Visit( node, e => BaseBinary( e ) ); } protected override Expression VisitBlock( Block node ) { return Visit( node, e => BaseBlock( e ) ); } protected override CatchBlock VisitCatchBlock( CatchBlock node ) { return Visit( node, e => BaseCatchBlock( e ) ); } protected override Expression VisitConditional( ConditionalExpression node ) { return Visit( node, e => BaseConditional( e ) ); } protected override Expression VisitConstant( ConstantExpression node ) { return Visit( node, e => BaseConstant( e ) ); } protected override Expression VisitDoWhile( DoStatement node ) { return Visit( node, e => BaseDoWhile( e ) ); } protected override Expression VisitDynamic( DynamicExpression node ) { return Visit( node, e => BaseDynamic( e ) ); } protected override ElementInit VisitElementInit( ElementInit initializer ) { return Visit( initializer, e => BaseElementInit( e ) ); } protected override Expression VisitEmpty( EmptyStatement node ) { return Visit( node, e => BaseEmpty( e ) ); } protected override Expression VisitExtension( Expression node ) { return Visit( node, e => BaseExtension( e ) ); } protected override Expression VisitGoto( GotoExpression node ) { return Visit( node, e => BaseGoto( e ) ); } protected override Expression VisitIndex( IndexExpression node ) { return Visit( node, e => BaseIndex( e ) ); } protected override Expression VisitInvocation( InvocationExpression node ) { return Visit( node, e => BaseInvocation( e ) ); } protected override Expression VisitLabel( LabelExpression node ) { return Visit( node, e => BaseLabel( e ) ); } protected override LabelTarget VisitLabelTarget( LabelTarget node ) { return Visit( node, e => BaseLabelTarget( e ) ); } protected override Expression VisitLambda( LambdaExpression node ) { return Visit( node, e => BaseLambda( e ) ); } protected override Expression VisitListInit( ListInitExpression node ) { return Visit( node, e => BaseListInit( e ) ); } protected override Expression VisitLoop( LoopStatement node ) { return Visit( node, e => BaseLoop( e ) ); } protected override Expression VisitMemberAccess( MemberExpression node ) { return Visit( node, e => BaseMemberAccess( e ) ); } protected override MemberAssignment VisitMemberAssignment( MemberAssignment assignment ) { return Visit( assignment, e => BaseMemberAssignment( e ) ); } protected override MemberBinding VisitMemberBinding( MemberBinding binding ) { return Visit( binding, e => BaseMemberBinding( e ) ); } protected override Expression VisitMemberInit( MemberInitExpression node ) { return Visit( node, e => BaseMemberInit( e ) ); } protected override MemberListBinding VisitMemberListBinding( MemberListBinding binding ) { return Visit( binding, e => BaseMemberListBinding( e ) ); } protected override MemberMemberBinding VisitMemberMemberBinding( MemberMemberBinding binding ) { return Visit( binding, e => BaseMemberMemberBinding( e ) ); } protected override Expression VisitMethodCall( MethodCallExpression node ) { return Visit( node, e => BaseMethodCall( e ) ); } protected override Expression VisitNew( NewExpression node ) { return Visit( node, e => BaseNew( e ) ); } protected override Expression VisitNewArray( NewArrayExpression node ) { return Visit( node, e => BaseNewArray( e ) ); } protected override Expression VisitParameter( ParameterExpression node ) { return Visit( node, e => BaseParameter( e ) ); } protected override Expression VisitReturn( ReturnStatement node ) { return Visit( node, e => BaseReturn( e ) ); } protected override Expression VisitRuntimeVariables( LocalScopeExpression node ) { return Visit( node, e => BaseRuntimeVariables( e ) ); } protected override Expression VisitScope( ScopeExpression node ) { return Visit( node, e => BaseScope( e ) ); } protected override Expression VisitSwitch( SwitchStatement node ) { return Visit( node, e => BaseSwitch( e ) ); } protected override SwitchCase VisitSwitchCase( SwitchCase node ) { return Visit( node, e => BaseSwitchCase( e ) ); } protected override Expression VisitThrow( ThrowStatement node ) { return Visit( node, e => BaseThrow( e ) ); } protected override Expression VisitTry( TryStatement node ) { return Visit( node, e => BaseTry( e ) ); } protected override Expression VisitTypeBinary( TypeBinaryExpression node ) { return Visit( node, e => BaseTypeBinary( e ) ); } protected override Expression VisitUnary( UnaryExpression node ) { return Visit( node, e => BaseUnary( e ) ); } #endregion #region Base invokers private Expression BaseAssignment( AssignmentExpression node ) { return base.VisitAssignment( node ); } protected Expression BaseBinary( BinaryExpression node ) { return base.VisitBinary( node ); } protected Expression BaseBlock( Block node ) { return base.VisitBlock( node ); } protected CatchBlock BaseCatchBlock( CatchBlock node ) { return base.VisitCatchBlock( node ); } protected Expression BaseConditional( ConditionalExpression node ) { return base.VisitConditional( node ); } protected Expression BaseConstant( ConstantExpression node ) { return base.VisitConstant( node ); } protected Expression BaseDoWhile( DoStatement node ) { return base.VisitDoWhile( node ); } protected Expression BaseDynamic( DynamicExpression node ) { return base.VisitDynamic( node ); } protected ElementInit BaseElementInit( ElementInit initializer ) { return base.VisitElementInit( initializer ); } protected Expression BaseEmpty( EmptyStatement node ) { return base.VisitEmpty( node ); } protected Expression BaseExtension( Expression node ) { return base.VisitExtension( node ); } protected Expression BaseGoto( GotoExpression node ) { return base.VisitGoto( node ); } protected Expression BaseIndex( IndexExpression node ) { return base.VisitIndex( node ); } protected Expression BaseInvocation( InvocationExpression node ) { return base.VisitInvocation( node ); } protected Expression BaseLabel( LabelExpression node ) { return base.VisitLabel( node ); } protected LabelTarget BaseLabelTarget( LabelTarget node ) { return base.VisitLabelTarget( node ); } protected Expression BaseLambda( LambdaExpression node ) { return base.VisitLambda( node ); } protected Expression BaseListInit( ListInitExpression node ) { return base.VisitListInit( node ); } protected Expression BaseLoop( LoopStatement node ) { return base.VisitLoop( node ); } protected Expression BaseMemberAccess( MemberExpression node ) { return base.VisitMemberAccess( node ); } protected MemberAssignment BaseMemberAssignment( MemberAssignment assignment ) { return base.VisitMemberAssignment( assignment ); } protected MemberBinding BaseMemberBinding( MemberBinding binding ) { return base.VisitMemberBinding( binding ); } protected Expression BaseMemberInit( MemberInitExpression node ) { return base.VisitMemberInit( node ); } protected MemberListBinding BaseMemberListBinding( MemberListBinding binding ) { return base.VisitMemberListBinding( binding ); } protected MemberMemberBinding BaseMemberMemberBinding( MemberMemberBinding binding ) { return base.VisitMemberMemberBinding( binding ); } protected Expression BaseMethodCall( MethodCallExpression node ) { return base.VisitMethodCall( node ); } protected Expression BaseNew( NewExpression node ) { return base.VisitNew( node ); } protected Expression BaseNewArray( NewArrayExpression node ) { return base.VisitNewArray( node ); } protected Expression BaseParameter( ParameterExpression node ) { return base.VisitParameter( node ); } protected Expression BaseReturn( ReturnStatement node ) { return base.VisitReturn( node ); } protected Expression BaseRuntimeVariables( LocalScopeExpression node ) { return base.VisitRuntimeVariables( node ); } protected Expression BaseScope( ScopeExpression node ) { return base.VisitScope( node ); } protected Expression BaseSwitch( SwitchStatement node ) { return base.VisitSwitch( node ); } protected SwitchCase BaseSwitchCase( SwitchCase node ) { return base.VisitSwitchCase( node ); } protected Expression BaseThrow( ThrowStatement node ) { return base.VisitThrow( node ); } protected Expression BaseTry( TryStatement node ) { return base.VisitTry( node ); } protected Expression BaseTypeBinary( TypeBinaryExpression node ) { return base.VisitTypeBinary( node ); } protected Expression BaseUnary( UnaryExpression node ) { return base.VisitUnary( node ); } #endregion } }


2 comments
Comments feed for this article
January 26, 2009 at 2:57 pm
Martin Maly
Hi Chris,
assuming you are using the DLR version found on codeplex … that has a public property “Dump” on every expression tree node which presents a string representaiton of the tree. Hopefully it will work for you.
Martin
January 26, 2009 at 3:17 pm
Chris Cavanagh
Martin – Thanks for that! I see there’s a public Dump (!) property in the latest DLR source, but it’s private in the 0.9 release. Am I looking in the right place?
When is the next DLR release planned for? Any breaking changes I need to be prepared for?