RhoMicro.CodeAnalysis.VisitorGenerator
24.1.8
Prefix Reserved
See the version list below for details.
dotnet add package RhoMicro.CodeAnalysis.VisitorGenerator --version 24.1.8
NuGet\Install-Package RhoMicro.CodeAnalysis.VisitorGenerator -Version 24.1.8
<PackageReference Include="RhoMicro.CodeAnalysis.VisitorGenerator" Version="24.1.8"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="RhoMicro.CodeAnalysis.VisitorGenerator" Version="24.1.8" />
<PackageReference Include="RhoMicro.CodeAnalysis.VisitorGenerator"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add RhoMicro.CodeAnalysis.VisitorGenerator --version 24.1.8
#r "nuget: RhoMicro.CodeAnalysis.VisitorGenerator, 24.1.8"
#:package RhoMicro.CodeAnalysis.VisitorGenerator@24.1.8
#addin nuget:?package=RhoMicro.CodeAnalysis.VisitorGenerator&version=24.1.8
#tool nuget:?package=RhoMicro.CodeAnalysis.VisitorGenerator&version=24.1.8
VisitorGenerator
Generates visitor pattern implementation for tree structures.
Example
The following example code illustrates using the generated visitor implementation by implementing a printer and interpreter for a simple language.
Define Tree Node Types
All node types to be included in the visitor implementations must be defined by annotating the abstract root node type with instances of the GenerateVisitorAttribute.
[GenerateVisitor<
AdditionExpression,
SubtractionExpression,
MultiplicationExpression,
DivisionExpression,
LiteralExpression,
AssignmentExpression,
VariableExpression,
StatementList>,
GenerateVisitor<
ExpressionStatement>]
internal abstract partial class SyntaxNode;
internal sealed partial class AssignmentExpression : Expression
{
public required String Name { get; init; }
public required Expression Expression { get; init; }
}
internal sealed partial class ExpressionStatement : Statement
{
public required Expression Expression { get; init; }
}
internal sealed partial class VariableExpression : Expression
{
public required String Name { get; init; }
}
internal sealed partial class LiteralExpression : Expression
{
public required Double Value { get; init; }
}
internal sealed partial class AdditionExpression : BinaryExpression;
internal sealed partial class SubtractionExpression : BinaryExpression;
internal sealed partial class MultiplicationExpression : BinaryExpression;
internal sealed partial class DivisionExpression : BinaryExpression;
internal abstract class BinaryExpression : Expression
{
public required Expression Lhs { get; init; }
public required Expression Rhs { get; init; }
}
internal sealed partial class StatementList : SyntaxNode
{
public required IEnumerable<Statement> Statements { get; init; }
}
internal abstract class Expression : SyntaxNode;
internal abstract class Statement : SyntaxNode;
Implement the Printer
The printer visitor is intended to simply print expressions to the console, without producing any results.
Note that it hooks into OnBeforeVisit and OnAfterVisit template methods in order to surround only binary expressions with parentheses.
Literals and variable expressions are not parenthesized.
internal sealed class Printer : SyntaxNodeVisitor
{
private Printer() { }
public static Printer Instance { get; } = new();
public override void OnBeforeVisitAdditionExpression(AdditionExpression target, CancellationToken cancellationToken = default)
=> Console.Write('(');
public override void TraverseAdditionExpression(AdditionExpression target, CancellationToken cancellationToken = default)
{
target.Lhs.Accept(this, cancellationToken);
Console.Write(" + ");
target.Rhs.Accept(this, cancellationToken);
}
public override void OnAfterVisitAdditionExpression(AdditionExpression target, CancellationToken cancellationToken = default)
=> Console.Write(')');
public override void OnBeforeVisitSubtractionExpression(SubtractionExpression target, CancellationToken cancellationToken = default)
=> Console.Write('(');
public override void TraverseSubtractionExpression(SubtractionExpression target, CancellationToken cancellationToken = default)
{
target.Lhs.Accept(this, cancellationToken);
Console.Write(" - ");
target.Rhs.Accept(this, cancellationToken);
}
public override void OnAfterVisitSubtractionExpression(SubtractionExpression target, CancellationToken cancellationToken = default)
=> Console.Write(')');
public override void OnBeforeVisitMultiplicationExpression(MultiplicationExpression target, CancellationToken cancellationToken = default)
=> Console.Write('(');
public override void TraverseMultiplicationExpression(MultiplicationExpression target, CancellationToken cancellationToken = default)
{
target.Lhs.Accept(this, cancellationToken);
Console.Write(" * ");
target.Rhs.Accept(this, cancellationToken);
}
public override void OnAfterVisitMultiplicationExpression(MultiplicationExpression target, CancellationToken cancellationToken = default)
=> Console.Write(')');
public override void OnBeforeVisitDivisionExpression(DivisionExpression target, CancellationToken cancellationToken = default)
=> Console.Write('(');
public override void TraverseDivisionExpression(DivisionExpression target, CancellationToken cancellationToken = default)
{
target.Lhs.Accept(this, cancellationToken);
Console.Write(" / ");
target.Rhs.Accept(this, cancellationToken);
}
public override void OnAfterVisitDivisionExpression(DivisionExpression target, CancellationToken cancellationToken = default)
=> Console.Write(')');
public override void VisitLiteralExpression(LiteralExpression target, CancellationToken cancellationToken = default)
=> Console.Write(target.Value.ToString(CultureInfo.InvariantCulture));
public override void OnAfterVisitExpressionStatement(ExpressionStatement target, CancellationToken cancellationToken = default)
=> Console.WriteLine();
public override void VisitAssignmentExpression(AssignmentExpression target, CancellationToken cancellationToken = default)
{
Console.Write($"{target.Name} = ");
target.Expression.Accept(this, cancellationToken);
}
public override void VisitVariableExpression(VariableExpression target, CancellationToken cancellationToken = default)
=> Console.Write(target.Name);
}
Implement the Interpreter
The interpreter is a simple treewalking interpreter that ambiently stores variable assignments in a hashmap. The last expression in the list of statements visited determines the result of the interpreter.
internal sealed class Interpreter : SyntaxNodeVisitor<Double>
{
protected override Double GetDefault() => 0;
private readonly Dictionary<String, Double> _variables = [];
private Double _lastStatementValue;
public override Double VisitLiteralExpression(LiteralExpression target, CancellationToken cancellationToken = default)
=> target.Value;
public override Double VisitAdditionExpression(AdditionExpression target, CancellationToken cancellationToken = default)
=> target.Lhs.Accept(this, cancellationToken) + target.Rhs.Accept(this, cancellationToken);
public override Double VisitSubtractionExpression(SubtractionExpression target, CancellationToken cancellationToken = default)
=> target.Lhs.Accept(this, cancellationToken) - target.Rhs.Accept(this, cancellationToken);
public override Double VisitMultiplicationExpression(MultiplicationExpression target, CancellationToken cancellationToken = default)
=> target.Lhs.Accept(this, cancellationToken) * target.Rhs.Accept(this, cancellationToken);
public override Double VisitDivisionExpression(DivisionExpression target, CancellationToken cancellationToken = default)
=> target.Lhs.Accept(this, cancellationToken) / target.Rhs.Accept(this, cancellationToken);
public override Double VisitAssignmentExpression(AssignmentExpression target, CancellationToken cancellationToken = default)
=> _variables[target.Name] = target.Expression.Accept(this, cancellationToken);
public override Double VisitVariableExpression(VariableExpression target, CancellationToken cancellationToken = default)
=> _variables.GetValueOrDefault(target.Name);
public override Double VisitExpressionStatement(ExpressionStatement target, CancellationToken cancellationToken = default)
=> _lastStatementValue = target.Expression.Accept(this, cancellationToken);
public override Double VisitStatementList(StatementList target, CancellationToken cancellationToken = default)
{
foreach(var statement in target.Statements)
_lastStatementValue = statement.Accept(this, cancellationToken);
return _lastStatementValue;
}
}
Define Ast
Define an Ast to test our visitor implementation:
var program = new StatementList()
{
Statements =
[
new ExpressionStatement()
{
Expression = new AssignmentExpression()
{
Name = "foo",
Expression = new AdditionExpression()
{
Lhs = new LiteralExpression() { Value = 6 },
Rhs = new LiteralExpression() { Value = 5 }
}
}
},
new ExpressionStatement()
{
Expression = new AssignmentExpression(){
Name = "bar",
Expression =new SubtractionExpression(){
Lhs = new DivisionExpression()
{
Lhs = new VariableExpression(){Name = "foo"},
Rhs = new MultiplicationExpression(){
Lhs = new LiteralExpression(){ Value = 6 },
Rhs = new LiteralExpression(){ Value = 0.5 }
}
},
Rhs = new DivisionExpression(){
Lhs= new LiteralExpression(){ Value = 2},
Rhs = new LiteralExpression(){Value = 3}
}
}
}
}
]
};
program.Accept(Printer.Instance);
var result = program.Accept(new Interpreter());
Console.WriteLine(result);
Observe Output
The first two lines are the result of our printer implementation, while the last line represents the interpreter result value.
foo = (6 + 5)
bar = ((foo / (6 * 0.5)) - (2 / 3))
3
Restrictions
- node base type must have arity 0
- node base type must be class or record class
- node types must inherit base node
- all nodes in hierarchy must be partial
- all nodes in hierarchy must be sealed or abstract
- nested types are disallowed
- generic node types are allowed
TODO
- implement analyzer to reflect restrictions
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.14.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 24.1.9 | 131 | 6/30/2026 |
| 24.1.8 | 116 | 6/14/2026 |
| 24.1.1 | 110 | 4/20/2026 |
| 24.1.0 | 135 | 1/26/2026 |
| 24.0.0 | 125 | 1/22/2026 |
| 23.2.2 | 123 | 1/4/2026 |
| 23.2.1 | 124 | 1/4/2026 |
| 23.1.0 | 182 | 12/21/2025 |
| 23.0.0 | 183 | 12/21/2025 |
| 22.0.3 | 243 | 8/20/2025 |
| 22.0.2 | 219 | 8/20/2025 |
| 22.0.1 | 208 | 7/12/2025 |
| 21.1.2 | 234 | 6/25/2025 |
| 21.1.0 | 226 | 6/25/2025 |
| 21.0.6 | 214 | 6/24/2025 |
| 1.0.0 | 227 | 6/22/2025 |