@@ -48,17 +48,25 @@ namespace FastExpressionCompiler.FlatExpression;
4848[ StructLayout ( LayoutKind . Sequential ) ]
4949public struct Idx : IEquatable < Idx >
5050{
51+ /// <summary>Raw 1-based index; 0 means nil.</summary>
5152 public int It ;
5253
54+ /// <summary>True when this index is nil (unset).</summary>
5355 public bool IsNil => It == 0 ;
56+ /// <summary>The nil sentinel value.</summary>
5457 public static Idx Nil => default ;
5558
59+ /// <summary>Creates a 1-based index from the given value.</summary>
5660 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
5761 public static Idx Of ( int oneBasedIndex ) => new Idx { It = oneBasedIndex } ;
5862
63+ /// <inheritdoc/>
5964 public bool Equals ( Idx other ) => It == other . It ;
65+ /// <inheritdoc/>
6066 public override bool Equals ( object obj ) => obj is Idx other && Equals ( other ) ;
67+ /// <inheritdoc/>
6168 public override int GetHashCode ( ) => It ;
69+ /// <inheritdoc/>
6270 public override string ToString ( ) => IsNil ? "nil" : It . ToString ( ) ;
6371}
6472
@@ -91,9 +99,13 @@ public struct Idx : IEquatable<Idx>
9199public struct ExpressionNode // 32 bytes: Type(8)+Info(8)+NodeType(4)+NextIdx(4)+ChildIdx(4)+ExtraIdx(4)
92100{
93101 // Reference fields placed first to avoid 4-byte padding that would appear after NodeType.
102+ /// <summary>Result type of this node.</summary>
94103 public Type Type ;
104+ /// <summary>Method/constructor for Call/New/Unary/Binary; parameter name for Parameter; closure key for Constant; parameter <see cref="Idx"/> array for Lambda.</summary>
95105 public object Info ;
106+ /// <summary>Expression kind (mirrors <see cref="System.Linq.Expressions.ExpressionType"/>).</summary>
96107 public ExpressionType NodeType ;
108+ /// <summary>Next sibling in an intrusive linked list (arguments, block expressions, etc.).</summary>
97109 public Idx NextIdx ;
98110 /// <summary>First child node, or for Constant with ExtraIdx.It==-1: raw int32 value bits.</summary>
99111 public Idx ChildIdx ;
@@ -110,13 +122,18 @@ public struct ExpressionNode // 32 bytes: Type(8)+Info(8)+NodeType(4)+NextIdx(4
110122public struct ExpressionTree
111123{
112124 // First 16 nodes are on the stack; further nodes spill to a heap array.
125+ /// <summary>Flat node storage. First 16 nodes are stack-resident; further nodes spill to a heap array.</summary>
113126 public SmallList < ExpressionNode , Stack16 < ExpressionNode > , NoArrayPool < ExpressionNode > > Nodes ;
114127 // First 4 closure constants on stack.
128+ /// <summary>Closure-captured constants. First 4 are stack-resident.</summary>
115129 public SmallList < object , Stack4 < object > , NoArrayPool < object > > ClosureConstants ;
130+ /// <summary>Index of the root expression node (typically a Lambda).</summary>
116131 public Idx RootIdx ;
117132
133+ /// <summary>Total number of nodes in this tree.</summary>
118134 public int NodeCount => Nodes . Count ;
119135
136+ /// <summary>Returns a reference to the node at the given index.</summary>
120137 [ UnscopedRef ]
121138 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
122139 public ref ExpressionNode NodeAt ( Idx idx )
@@ -155,7 +172,7 @@ private static int ToInt32Bits(object value, Type t)
155172 if ( t == typeof ( int ) ) return ( int ) value ;
156173 if ( t == typeof ( uint ) ) return ( int ) ( uint ) value ; // reinterpret bits
157174 if ( t == typeof ( bool ) ) return ( bool ) value ? 1 : 0 ;
158- if ( t == typeof ( float ) ) return BitConverter . SingleToInt32Bits ( ( float ) value ) ;
175+ if ( t == typeof ( float ) ) return FloatIntBits . FloatToInt ( ( float ) value ) ;
159176 if ( t == typeof ( byte ) ) return ( byte ) value ;
160177 if ( t == typeof ( sbyte ) ) return ( sbyte ) value ;
161178 if ( t == typeof ( short ) ) return ( short ) value ;
@@ -170,7 +187,7 @@ internal static object FromInt32Bits(int bits, Type t)
170187 if ( t == typeof ( int ) ) return bits ;
171188 if ( t == typeof ( uint ) ) return ( uint ) bits ;
172189 if ( t == typeof ( bool ) ) return bits != 0 ;
173- if ( t == typeof ( float ) ) return BitConverter . Int32BitsToSingle ( bits ) ;
190+ if ( t == typeof ( float ) ) return FloatIntBits . IntToFloat ( bits ) ;
174191 if ( t == typeof ( byte ) ) return ( byte ) bits ;
175192 if ( t == typeof ( sbyte ) ) return ( sbyte ) bits ;
176193 if ( t == typeof ( short ) ) return ( short ) bits ;
@@ -179,11 +196,22 @@ internal static object FromInt32Bits(int bits, Type t)
179196 return null ; // unreachable
180197 }
181198
199+ // Explicit-layout union to reinterpret float/int bits without Unsafe or BitConverter (portable across all TFMs).
200+ [ StructLayout ( LayoutKind . Explicit ) ]
201+ private struct FloatIntBits
202+ {
203+ [ FieldOffset ( 0 ) ] public float F ;
204+ [ FieldOffset ( 0 ) ] public int I ;
205+ public static int FloatToInt ( float f ) => new FloatIntBits { F = f } . I ;
206+ public static float IntToFloat ( int i ) => new FloatIntBits { I = i } . F ;
207+ }
208+
182209 // Types not fitting in int32 but still safe to keep inline in Info (no special closure treatment needed).
183210 private static bool IsInfoInline ( Type t ) =>
184211 t == typeof ( string ) || t == typeof ( long ) || t == typeof ( double ) ||
185212 t == typeof ( decimal ) || t == typeof ( DateTime ) || t == typeof ( Guid ) ;
186213
214+ /// <summary>Adds a Constant node. Small value types (int, bool, float, etc.) are stored inline without boxing.</summary>
187215 public Idx Constant ( object value , bool putIntoClosure = false )
188216 {
189217 if ( value == null )
@@ -207,54 +235,70 @@ public Idx Constant(object value, bool putIntoClosure = false)
207235 return AddNode ( ExpressionType . Constant , type , extraIdx : new Idx { It = ci + 1 } ) ;
208236 }
209237
238+ /// <summary>Typed overload of <see cref="Constant(object,bool)"/>.</summary>
210239 public Idx Constant < T > ( T value , bool putIntoClosure = false ) =>
211240 Constant ( ( object ) value , putIntoClosure ) ;
212241
242+ /// <summary>Adds a Parameter node with the given type and optional name.</summary>
213243 public Idx Parameter ( Type type , string name = null ) =>
214244 AddNode ( ExpressionType . Parameter , type , info : name ) ;
215245
246+ /// <summary>Alias for <see cref="Parameter"/> — adds a block-local variable node.</summary>
216247 public Idx Variable ( Type type , string name = null ) =>
217248 AddNode ( ExpressionType . Parameter , type , info : name ) ;
218249
250+ /// <summary>Adds a Default(type) node.</summary>
219251 public Idx Default ( Type type ) =>
220252 AddNode ( ExpressionType . Default , type ) ;
221253
254+ /// <summary>Adds a unary expression node.</summary>
222255 public Idx Unary ( ExpressionType nodeType , Idx operand , Type type , MethodInfo method = null ) =>
223256 AddNode ( nodeType , type , info : method , childIdx : operand ) ;
224257
258+ /// <summary>Adds a Convert node.</summary>
225259 public Idx Convert ( Idx operand , Type toType ) =>
226260 Unary ( ExpressionType . Convert , operand , toType ) ;
227261
262+ /// <summary>Adds a Not node.</summary>
228263 public Idx Not ( Idx operand ) =>
229264 Unary ( ExpressionType . Not , operand , typeof ( bool ) ) ;
230265
266+ /// <summary>Adds a Negate node.</summary>
231267 public Idx Negate ( Idx operand , Type type ) =>
232268 Unary ( ExpressionType . Negate , operand , type ) ;
233269
270+ /// <summary>Adds a binary expression node.</summary>
234271 public Idx Binary ( ExpressionType nodeType , Idx left , Idx right , Type type , MethodInfo method = null ) =>
235272 AddNode ( nodeType , type , info : method , childIdx : left , extraIdx : right ) ;
236273
274+ /// <summary>Adds an Add node.</summary>
237275 public Idx Add ( Idx left , Idx right , Type type ) =>
238276 Binary ( ExpressionType . Add , left , right , type ) ;
239277
278+ /// <summary>Adds a Subtract node.</summary>
240279 public Idx Subtract ( Idx left , Idx right , Type type ) =>
241280 Binary ( ExpressionType . Subtract , left , right , type ) ;
242281
282+ /// <summary>Adds a Multiply node.</summary>
243283 public Idx Multiply ( Idx left , Idx right , Type type ) =>
244284 Binary ( ExpressionType . Multiply , left , right , type ) ;
245285
286+ /// <summary>Adds an Equal node (returns bool).</summary>
246287 public Idx Equal ( Idx left , Idx right ) =>
247288 Binary ( ExpressionType . Equal , left , right , typeof ( bool ) ) ;
248289
290+ /// <summary>Adds an Assign node.</summary>
249291 public Idx Assign ( Idx target , Idx value , Type type ) =>
250292 Binary ( ExpressionType . Assign , target , value , type ) ;
251293
294+ /// <summary>Adds a New node calling the given constructor with the provided arguments.</summary>
252295 public Idx New ( ConstructorInfo ctor , params Idx [ ] args )
253296 {
254297 var firstArgIdx = LinkList ( args ) ;
255298 return AddNode ( ExpressionType . New , ctor . DeclaringType , info : ctor , childIdx : firstArgIdx ) ;
256299 }
257300
301+ /// <summary>Adds a Call node. Pass <see cref="Idx.Nil"/> for <paramref name="instance"/> for static calls.</summary>
258302 public Idx Call ( MethodInfo method , Idx instance , params Idx [ ] args )
259303 {
260304 var returnType = method . ReturnType == typeof ( void ) ? typeof ( void ) : method . ReturnType ;
@@ -266,6 +310,7 @@ public Idx Call(MethodInfo method, Idx instance, params Idx[] args)
266310
267311 // Parameters stored in Info as Idx[] rather than chained via NextIdx, because the same
268312 // parameter node may already have its NextIdx used as part of a New/Call argument chain.
313+ /// <summary>Adds a Lambda node. Sets <see cref="RootIdx"/> when <paramref name="isRoot"/> is true.</summary>
269314 public Idx Lambda ( Type delegateType , Idx body , Idx [ ] parameters = null , bool isRoot = true )
270315 {
271316 var lambdaIdx = AddNode ( ExpressionType . Lambda , delegateType , info : parameters , childIdx : body ) ;
@@ -274,19 +319,22 @@ public Idx Lambda(Type delegateType, Idx body, Idx[] parameters = null, bool isR
274319 return lambdaIdx ;
275320 }
276321
322+ /// <summary>Adds a Conditional (ternary) node.</summary>
277323 public Idx Conditional ( Idx test , Idx ifTrue , Idx ifFalse , Type type )
278324 {
279325 NodeAt ( ifTrue ) . NextIdx = ifFalse ; // ifFalse hangs off ifTrue.NextIdx
280326 return AddNode ( ExpressionType . Conditional , type , childIdx : test , extraIdx : ifTrue ) ;
281327 }
282328
329+ /// <summary>Adds a Block node containing the given expressions and optional block-local variables.</summary>
283330 public Idx Block ( Type type , Idx [ ] exprs , Idx [ ] variables = null )
284331 {
285332 var firstExprIdx = LinkList ( exprs ) ;
286333 var firstVarIdx = variables == null || variables . Length == 0 ? Idx . Nil : LinkList ( variables ) ;
287334 return AddNode ( ExpressionType . Block , type , childIdx : firstExprIdx , extraIdx : firstVarIdx ) ;
288335 }
289336
337+ /// <summary>Chains the given indices via <see cref="ExpressionNode.NextIdx"/> and returns the first index.</summary>
290338 public Idx LinkList ( Idx [ ] indices )
291339 {
292340 if ( indices == null || indices . Length == 0 )
@@ -298,6 +346,7 @@ public Idx LinkList(Idx[] indices)
298346 }
299347
300348 // Allocates an enumerator — suitable for tests and diagnostics; avoid in hot paths.
349+ /// <summary>Enumerates the sibling chain starting at <paramref name="head"/>. Allocates an enumerator — avoid in hot paths.</summary>
301350 public IEnumerable < Idx > Siblings ( Idx head )
302351 {
303352 var cur = head ;
@@ -309,6 +358,7 @@ public IEnumerable<Idx> Siblings(Idx head)
309358 }
310359
311360 // Builds body after registering params so they are found in paramMap when encountered in the body.
361+ /// <summary>Converts this flat tree to a <see cref="System.Linq.Expressions.Expression"/> rooted at <see cref="RootIdx"/>.</summary>
312362 public SysExpr ToSystemExpression ( )
313363 {
314364 var paramMap = default ( SmallMap16 < int , SysParam , IntEq > ) ;
@@ -459,6 +509,7 @@ private List<SysExpr> SiblingList(Idx head, ref SmallMap16<int, SysParam, IntEq>
459509 }
460510
461511 // O(n) structural equality — no traversal, single pass over the flat arrays.
512+ /// <summary>O(n) structural equality check. Compares both trees node-by-node in a single pass — no recursive traversal.</summary>
462513 public static bool StructurallyEqual ( ref ExpressionTree a , ref ExpressionTree b )
463514 {
464515 if ( a . NodeCount != b . NodeCount ) return false ;
@@ -494,6 +545,7 @@ private static bool InfoEqual(object infoA, object infoB)
494545 return Equals ( infoA , infoB ) ;
495546 }
496547
548+ /// <summary>Returns a human-readable dump of all nodes and closure constants for diagnostics.</summary>
497549 public string Dump ( )
498550 {
499551 var sb = new System . Text . StringBuilder ( ) ;
0 commit comments