ktsu.Coder.Graph
3.0.0
Prefix Reserved
See the version list below for details.
dotnet add package ktsu.Coder.Graph --version 3.0.0
NuGet\Install-Package ktsu.Coder.Graph -Version 3.0.0
<PackageReference Include="ktsu.Coder.Graph" Version="3.0.0" />
<PackageVersion Include="ktsu.Coder.Graph" Version="3.0.0" />
<PackageReference Include="ktsu.Coder.Graph" />
paket add ktsu.Coder.Graph --version 3.0.0
#r "nuget: ktsu.Coder.Graph, 3.0.0"
#:package ktsu.Coder.Graph@3.0.0
#addin nuget:?package=ktsu.Coder.Graph&version=3.0.0
#tool nuget:?package=ktsu.Coder.Graph&version=3.0.0
ktsu.Coder
A flexible and extensible .NET library for representing code as Abstract Syntax Trees (AST), serializing to YAML, and generating code in multiple programming languages.
Overview
ktsu.Coder provides a language-agnostic way to represent code structures using Abstract Syntax Trees. The library allows you to:
- Build AST structures programmatically
- Serialize AST to human-readable YAML format
- Generate code in supported programming languages
- Perform round-trip serialization/deserialization
This makes it ideal for code generation tools, transpilers, and any application that needs to work with code structures in a language-independent way.
Features
- Language-Agnostic AST: Represent code structures without being tied to a specific programming language
- YAML Serialization: Human-readable serialization format that's perfect for version control and diffs
- Extensible Language Support: Plugin-based architecture for adding new target languages
- Deep Cloning: Full support for cloning AST structures
- Type Safety: Strongly-typed AST nodes with compile-time safety
- Metadata Support: Attach custom metadata to any AST node
Supported AST Node Types
- ClassDeclaration: A named class with an optional base type, holding methods, fields and nested classes
- EntryPoint: Where the program starts running, optionally reading arguments and returning an exit code
- FunctionDeclaration: Represents function/method declarations with parameters and body
- Parameter: Function parameters with optional default values
- ReturnStatement: Return statements with optional expressions
- VariableDeclaration: Variable declarations, optionally constant or type-inferred
- AssignmentStatement: Assignments, including the compound operators (
+=,<<=, …) - BinaryExpression: Two operands and an operator (
a + b,x == y,p && q) - UnaryExpression: One operator applied to one operand (
-x,!ready,~mask) - VariableReference: A reference to a variable by name
- LiteralExpression<T>: Typed literals (string, int, bool, double)
- AstLeafNode<T>: Generic leaf nodes for literals (strings, numbers, booleans)
Every operator in UnaryOperator and BinaryOperator exists in all four target languages, so no
AST built from them is untranslatable. Only the spelling varies — Python's not/and/or,
JavaScript's strict ===/!== — and each generator overrides just the operators it spells
differently.
Increment and decrement are deliberately absent from UnaryOperator: Python has no spelling for
them, and an AssignmentStatement with AssignmentOperator.AddAssign expresses the same effect in
every target language.
Visibility
A ClassDeclaration, a FunctionDeclaration and a VariableDeclaration each carry a Visibility:
Public, Protected, Internal, Private, or Unspecified — the default, meaning the
declaration is written the way the target language would write it anyway. It is an enumeration
rather than the modifier's text because no two languages spell visibility the same way:
| Language | How it is spelled |
|---|---|
| C# | The keyword, in front of the declaration; a class or function with none is public |
| C++ | An access label (public:, protected:, private:) the members are grouped under; Internal becomes public: |
| JavaScript | A private class member takes the # prefix, which is JavaScript's own private syntax; nothing for the rest |
| Python | Nothing — Python has no access modifiers, and its leading-underscore convention renames the declaration rather than modifying it |
Constants and entry points
A VariableDeclaration marked IsConstant with a literal InitialValue is a constant. C# writes
const, C++ writes const for a local and static constexpr for a class member, JavaScript writes
const for a local and static for a class member, and Python writes a plain assignment, having no
constant declaration to spell.
An EntryPoint holds the statements a program runs. Each generator writes the spelling its language
looks for: C#'s static Main, C++'s free int main, Python's main with the __main__ guard that
calls it (and the import sys its arguments and exit code need), and JavaScript's main with the
call that runs it.
Visual graph editor
ktsu.Coder.Graph renders an AST as an editable node graph, built on
ktsu.ImGui.NodeEditor with its force-directed layout.
AstGraphEditor editor = new(functionDeclaration);
// once per frame, inside your ImGui render loop
editor.Draw(new Vector2(1200, 800), deltaTime);
foreach (AstGraphProblem problem in editor.Problems)
{
Console.WriteLine(problem.Message);
}
Each AST node becomes one editor node with a single output pin — itself — and one input pin per slot it can hold a child in, so a link reads "this node fills that slot of that parent". Right-click for the palette, drag between pins to connect, Delete to remove, and Undo/Redo on the toolbar. The palette lists every binary, unary and assignment operator, so which expression to create is a choice made when creating it.
Selecting a node opens it in the inspector — the panel under the canvas — which edits everything
about it that a link cannot express — a literal's value, a declaration's name and type, an expression's operator — and each edit
is one step on the undo stack. AstFields is the model behind it: a node's editable properties as
named fields of a kind, so a caller building its own UI does not need a panel per node type.
foreach (AstField field in AstFields.Of(node))
{
Console.WriteLine($"{field.Name} = {field.Value} ({field.Kind})");
}
AstFields.TryWrite(node, "Operator", nameof(BinaryOperator.Multiply));
A slot that holds a sequence — a function's parameters, its body, a class's members — has + and
- buttons in the inspector, so the number of them is changed without dragging nodes in from the
palette. "Convert to" replaces a node with a different kind in place, moving the operands the new
one can take across and keeping the rest rather than discarding them.
The origin of the graph's space is the middle of the canvas, and it follows the window as that is resized. Everything measured against it agrees as a result: gravity holds an untouched arrangement in the middle of the view, a document arrives centred on the frame it first appears, a node with no position of its own is seeded near the middle rather than in a corner, and "Fit" re-centres an arrangement that has been dragged away without disturbing its shape.
The AST is the document and the graph is a view of it: every edit is applied to the AST and the
graph rebuilt from it, preserving on-screen positions by node identity. A graph is allowed to be
incomplete while you work — AstGraphEditor.Problems (or AstGraph.Validate()) lists outstanding
operands and disconnected nodes rather than throwing, so consult it before handing the AST to a
generator.
The package targets net10.0 only, since the node editor does. ktsu.Coder itself has no UI
dependency and continues to cross-target.
Editor application
Coder.Editor is a desktop application built on ktsu.ImGui.App: the document as a node graph on
the left, the code it generates on the right, and a File menu for New function / New class / Open /
Save / Export with a recent files list. Ctrl+Z, Ctrl+Shift+Z, Ctrl+Y and Ctrl+S work wherever the
keyboard focus is.
dotnet run --project Coder.Editor
Documents are .coder.yaml files — the same YAML the serializer already round-trips, so anything the
library can write, the editor can open. The code pane follows the document live and lists what is
outstanding instead of generating while an operand is unfilled; clicking one of those selects the
node it is about. Generated source can be copied to the clipboard or exported beside the document in
the extension of whichever language is being previewed.
It uses the ktsu.Essentials providers where they fit rather than reaching for System.IO directly:
IFileSystemProvider for reading and writing documents, and an IPersistenceProvider over the XDG
config directory for the recent-files list and the preview language. Neither location is a decision
the editor makes for itself.
Supported Target Languages
Resolve IEnumerable<ILanguageGenerator> and select on LanguageId, or construct a generator
directly.
LanguageId |
Generator | Extension | Notes |
|---|---|---|---|
python |
PythonGenerator |
py |
Type hints, None for void, pass for an empty body or class; self on methods; main with its __main__ guard |
csharp |
CSharpGenerator |
cs |
Mapped type names, var for inferred declarations, visibility keywords, const, static Main |
javascript |
JavaScriptGenerator |
js |
Untyped; const/let; strict === and !==; method, static and #private syntax inside a class |
cpp |
CppGenerator |
cpp |
Mapped type spellings (str → std::string); auto for inferred declarations; access labels, static constexpr members and a terminating ; on a class |
Installation
Add the NuGet package:
dotnet add package ktsu.Coder
Releases through 1.8.2 were published as ktsu.Coder.Core; from 2.0.0 the package ID is
ktsu.Coder. The namespaces (ktsu.Coder.Ast, ktsu.Coder.Languages, ktsu.Coder.Serialization)
are unchanged, so migrating is a package reference edit and nothing more.
Quick Start
Creating an AST
using ktsu.Coder.Ast;
using ktsu.Coder.Languages;
using ktsu.Coder.Serialization;
// Create a function declaration
var function = new FunctionDeclaration("calculate_sum")
{
ReturnType = "int"
};
// Add parameters
function.Parameters.Add(new Parameter("a", "int"));
function.Parameters.Add(new Parameter("b", "int"));
function.Parameters.Add(new Parameter("debug", "bool")
{
IsOptional = true,
DefaultValue = "False"
});
// Add function body
function.Body.Add(new ReturnStatement(new AstLeafNode<string>("a + b")));
Serializing to YAML
var serializer = new YamlSerializer();
string yamlContent = serializer.Serialize(function);
Console.WriteLine(yamlContent);
Output:
functionDeclaration:
name: calculate_sum
returnType: int
parameters:
- name: a
type: int
- name: b
type: int
- name: debug
type: bool
isOptional: true
defaultValue: False
body:
- returnStatement:
expression:
Leaf<String>: a + b
Generating Code
var pythonGenerator = new PythonGenerator();
string pythonCode = pythonGenerator.Generate(function);
Console.WriteLine(pythonCode);
Output:
def calculate_sum(a: int, b: int, debug: bool = False) -> int:
return "a + b"
Round-trip Serialization
// Deserialize from YAML
var deserializer = new YamlDeserializer();
AstNode deserializedAst = deserializer.Deserialize(yamlContent);
// Generate code from deserialized AST
string regeneratedCode = pythonGenerator.Generate(deserializedAst);
Architecture
The library follows SOLID principles with a clean separation of concerns:
- AST Layer: Language-agnostic representation of code structures
- Serialization Layer: YAML serialization/deserialization
- Language Layer: Pluggable code generators for specific languages
Extending with New Languages
To add support for a new language, implement the ILanguageGenerator interface:
public class JavaScriptGenerator : LanguageGeneratorBase
{
public override string LanguageId => "javascript";
public override string DisplayName => "JavaScript";
public override string FileExtension => "js";
protected override void GenerateInternal(AstNode node, StringBuilder builder, int indentLevel)
{
// Implementation for JavaScript code generation
}
}
Examples
The repository includes a sample console application:
- Coder.Cli: Command-line tool demonstrating the AST, YAML round-tripping and code generation
Run it to see the library in action:
dotnet run --project Coder.Cli
For the interactive experience, run the desktop editor instead:
dotnet run --project Coder.Editor
Contributing
Contributions are welcome! Please feel free to submit pull requests or open issues for:
- New language generators
- Additional AST node types
- Bug fixes and improvements
- Documentation enhancements
License
MIT License. Copyright (c) ktsu.dev
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Hexa.NET.ImGui (>= 2.2.9)
- Hexa.NET.ImNodes (>= 2.2.9)
- ktsu.Coder (>= 3.0.0)
- ktsu.ForceDirectedLayout (>= 3.25.0)
- ktsu.ImGui.NodeEditor (>= 3.25.0)
- ktsu.UndoRedo.Core (>= 1.0.19)
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 |
|---|---|---|
| 3.2.0 | 10 | 9/9/2026 |
| 3.1.3 | 26 | 9/9/2026 |
| 3.1.2 | 36 | 9/9/2026 |
| 3.1.1 | 33 | 9/9/2026 |
| 3.1.0 | 41 | 9/8/2026 |
| 3.0.0 | 52 | 9/8/2026 |
| 2.1.3 | 52 | 9/8/2026 |
| 2.1.2 | 47 | 9/8/2026 |
| 2.1.1 | 54 | 9/8/2026 |
| 2.1.0 | 48 | 9/8/2026 |
| 2.0.0 | 50 | 9/8/2026 |
| 1.8.3 | 53 | 9/8/2026 |
| 1.8.2 | 58 | 9/8/2026 |
| 1.8.1 | 50 | 9/8/2026 |
| 1.8.0 | 49 | 9/8/2026 |
| 1.7.0 | 49 | 9/8/2026 |
| 1.6.0 | 53 | 9/8/2026 |
| 1.5.0 | 68 | 9/7/2026 |
| 1.4.0 | 62 | 9/7/2026 |
| 1.3.0 | 66 | 9/7/2026 |
## v3.0.0 (major)
Changes since v2.0.0:
- [major] Model visibility, constants and entry points in the AST ([@Claude](https://github.com/Claude))
- Take the ImGui suite at 3.25.0 ([@Claude](https://github.com/Claude))
- Take the ImGui suite at 3.24.0 ([@Claude](https://github.com/Claude))
- Take the ImGui suite at 3.23.0 ([@Claude](https://github.com/Claude))
- Take the ImGui suite at 3.22.0 ([@Claude](https://github.com/Claude))
- Merge remote-tracking branch 'origin/main' into claude/default-document-class ([@Claude](https://github.com/Claude))
- Rename the round-trip test's local root so it stops shadowing the fixture ([@Claude](https://github.com/Claude))
- Open on a class that does something, and fix loading an assignment ([@Claude](https://github.com/Claude))