Editor/Prism/Compiler/Ir/IrStmt.cs

IR statement types for the Prism compiler in the Editor project. Defines an abstract IrStmt with concrete records for declaration, assignment, control flow (if, for, while, break, continue, return), discard, expression statements, comments and scoped blocks, plus IrBlock which holds ordered statements and helpers to add them.

File Access
using Editor.Prism.Core;

namespace Editor.Prism.Compiler.Ir;

/// <summary>
/// A statement.
/// <para>
/// <see cref="Origin"/> — the node that produced this statement — is on every single one, and it is
/// the whole source map. The backend records <c>emitted line -&gt; Origin</c> as it writes; inverting
/// that is what turns a raw compiler line number into a highlighted node.
/// </para>
/// </summary>
public abstract record IrStmt( NodeId Origin );

/// <summary>An ordered list of statements with its own scope.</summary>
public sealed class IrBlock
{
	/// <summary>Statements in emission order.</summary>
	public List<IrStmt> Statements { get; } = new();

	/// <summary>True when the block emits nothing.</summary>
	public bool IsEmpty => Statements.Count == 0;

	/// <summary>Append a statement. Null is ignored.</summary>
	public IrBlock Add( IrStmt statement )
	{
		if ( statement is not null ) Statements.Add( statement );
		return this;
	}

	/// <summary>Append a range of statements.</summary>
	public IrBlock AddRange( IEnumerable<IrStmt> statements )
	{
		if ( statements is null ) return this;

		foreach ( var statement in statements ) Add( statement );
		return this;
	}

	/// <inheritdoc/>
	public override string ToString() => $"{{ {Statements.Count} statements }}";
}

/// <summary>Declare a local and initialise it. The workhorse: every non-trivial expression binds to one.</summary>
public sealed record IrDecl( NodeId Origin, string Name, ShaderType Type, IrExpr Init ) : IrStmt( Origin );

/// <summary>Assign to an existing location.</summary>
public sealed record IrAssign( NodeId Origin, IrExpr Target, IrExpr Value ) : IrStmt( Origin );

/// <summary>Real branching. Only the taken side executes, unlike a ternary.</summary>
public sealed record IrIf( NodeId Origin, IrExpr Cond, IrBlock Then, IrBlock Else ) : IrStmt( Origin );

/// <summary>A counted loop over <c>[0, Count)</c>.</summary>
public sealed record IrFor( NodeId Origin, string Var, IrExpr Count, IrBlock Body ) : IrStmt( Origin );

/// <summary>A conditional loop.</summary>
public sealed record IrWhile( NodeId Origin, IrExpr Cond, IrBlock Body ) : IrStmt( Origin );

/// <summary>Leave the innermost loop.</summary>
public sealed record IrBreak( NodeId Origin ) : IrStmt( Origin );

/// <summary>Skip to the next iteration of the innermost loop.</summary>
public sealed record IrContinue( NodeId Origin ) : IrStmt( Origin );

/// <summary>Return a value from the enclosing function.</summary>
public sealed record IrReturn( NodeId Origin, IrExpr Value ) : IrStmt( Origin );

/// <summary>Kill the fragment. Pixel stage only.</summary>
public sealed record IrDiscard( NodeId Origin ) : IrStmt( Origin );

/// <summary>
/// Evaluate an expression for its side effects, e.g. <c>clip(...)</c> or an interlocked operation.
/// </summary>
public sealed record IrExprStmt( NodeId Origin, IrExpr Value ) : IrStmt( Origin );

/// <summary>A comment. Emitted only when the graph has comments or debug symbols enabled.</summary>
public sealed record IrComment( NodeId Origin, string Text ) : IrStmt( Origin );

/// <summary>A nested scope, used to keep temp names from colliding across inlined subgraphs.</summary>
public sealed record IrScope( NodeId Origin, IrBlock Body ) : IrStmt( Origin );