Skip to content

Commit a519e72

Browse files
konardclaude
andcommitted
Implement ANTLR AST transformation with exact position mapping in links store
- Add ANTLR4 package dependency to Storage project - Create AstNode class with complete text position mapping (line, column, character positions) - Implement CSharpAstTransformer for basic C# language constructs - Integrate AST storage into FileStorage with links store backend - Add comprehensive test suite with 6 passing tests - Include usage examples and documentation - Map each AST node to exact location in source code text as required by issue #77 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 9a015fb commit a519e72

File tree

9 files changed

+838
-0
lines changed

9 files changed

+838
-0
lines changed

Bot.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage", "csharp\Storage\S
1010
EndProject
1111
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraderBot", "csharp\TraderBot\TraderBot.csproj", "{FAE89FE2-17C5-4AD6-98EC-84002CC4C672}"
1212
EndProject
13+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.Tests", "csharp\Storage.Tests\Storage.Tests.csproj", "{B8E5F9A1-2D34-4E7F-9F8C-1A2B3C4D5E6F}"
14+
EndProject
1315
Global
1416
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1517
Debug|Any CPU = Debug|Any CPU
@@ -36,5 +38,9 @@ Global
3638
{FAE89FE2-17C5-4AD6-98EC-84002CC4C672}.Debug|Any CPU.Build.0 = Debug|Any CPU
3739
{FAE89FE2-17C5-4AD6-98EC-84002CC4C672}.Release|Any CPU.ActiveCfg = Release|Any CPU
3840
{FAE89FE2-17C5-4AD6-98EC-84002CC4C672}.Release|Any CPU.Build.0 = Release|Any CPU
41+
{B8E5F9A1-2D34-4E7F-9F8C-1A2B3C4D5E6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42+
{B8E5F9A1-2D34-4E7F-9F8C-1A2B3C4D5E6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
43+
{B8E5F9A1-2D34-4E7F-9F8C-1A2B3C4D5E6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
44+
{B8E5F9A1-2D34-4E7F-9F8C-1A2B3C4D5E6F}.Release|Any CPU.Build.0 = Release|Any CPU
3945
EndGlobalSection
4046
EndGlobal
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using System;
2+
using System.IO;
3+
using Xunit;
4+
using Storage.Local;
5+
using Storage.AST;
6+
7+
namespace Storage.Tests
8+
{
9+
public class AstTransformationTests : IDisposable
10+
{
11+
private readonly string _testDbPath;
12+
private readonly FileStorage _storage;
13+
14+
public AstTransformationTests()
15+
{
16+
_testDbPath = Path.GetTempFileName();
17+
_storage = new FileStorage(_testDbPath);
18+
}
19+
20+
[Fact]
21+
public void TransformCodeToAst_WithSimpleCode_ShouldCreateAstInLinksStore()
22+
{
23+
// Arrange
24+
var code = @"using System;
25+
namespace TestNamespace
26+
{
27+
public class TestClass
28+
{
29+
public void TestMethod()
30+
{
31+
Console.WriteLine(""Hello World"");
32+
}
33+
}
34+
}";
35+
36+
// Act
37+
var rootAstNodeLink = _storage.TransformCodeToAst(code);
38+
39+
// Assert
40+
Assert.NotEqual(0UL, rootAstNodeLink);
41+
42+
var astNodes = _storage.GetAllAstNodes();
43+
Assert.NotEmpty(astNodes);
44+
45+
var rootNodeInfo = _storage.GetAstNodeInfo(rootAstNodeLink);
46+
Assert.Equal("CompilationUnit", rootNodeInfo["NodeType"]);
47+
Assert.False(rootNodeInfo.ContainsKey("Error"));
48+
}
49+
50+
[Fact]
51+
public void CSharpAstTransformer_WithSimpleCode_ShouldMapNodesToExactPositions()
52+
{
53+
// Arrange
54+
var transformer = new CSharpAstTransformer();
55+
var code = "using System;\nnamespace Test {}";
56+
57+
// Act
58+
var rootNode = transformer.TransformCode(code);
59+
60+
// Assert
61+
Assert.Equal("CompilationUnit", rootNode.NodeType);
62+
Assert.Equal(0, rootNode.StartPosition);
63+
Assert.Equal(code.Length - 1, rootNode.EndPosition);
64+
Assert.Equal(1, rootNode.StartLine);
65+
Assert.Equal(2, rootNode.EndLine);
66+
67+
// Check that children are created
68+
Assert.NotEmpty(rootNode.Children);
69+
70+
// Verify all nodes have position information
71+
var allNodes = transformer.GetAllNodesWithPositions(rootNode);
72+
foreach (var node in allNodes)
73+
{
74+
Assert.True(node.StartPosition >= 0);
75+
Assert.True(node.EndPosition >= node.StartPosition);
76+
Assert.True(node.StartLine >= 1);
77+
Assert.True(node.StartColumn >= 0);
78+
}
79+
}
80+
81+
[Fact]
82+
public void AstNode_WithChildNodes_ShouldMaintainParentChildRelationships()
83+
{
84+
// Arrange
85+
var parent = new AstNode { NodeType = "Parent", Text = "parent content" };
86+
var child1 = new AstNode { NodeType = "Child1", Text = "child1 content" };
87+
var child2 = new AstNode { NodeType = "Child2", Text = "child2 content" };
88+
89+
// Act
90+
parent.AddChild(child1);
91+
parent.AddChild(child2);
92+
93+
// Assert
94+
Assert.Equal(2, parent.Children.Count);
95+
Assert.Equal(parent, child1.Parent);
96+
Assert.Equal(parent, child2.Parent);
97+
Assert.Equal(0, parent.Depth);
98+
Assert.Equal(1, child1.Depth);
99+
Assert.Equal(1, child2.Depth);
100+
}
101+
102+
[Fact]
103+
public void TransformCodeToAst_WithEmptyCode_ShouldThrowArgumentException()
104+
{
105+
// Act & Assert
106+
Assert.Throws<ArgumentException>(() => _storage.TransformCodeToAst(""));
107+
Assert.Throws<ArgumentException>(() => _storage.TransformCodeToAst(null));
108+
}
109+
110+
[Fact]
111+
public void GetAstNodeInfo_WithValidAstNode_ShouldReturnNodeInformation()
112+
{
113+
// Arrange
114+
var code = "public class TestClass {}";
115+
var rootAstNodeLink = _storage.TransformCodeToAst(code);
116+
117+
// Act
118+
var nodeInfo = _storage.GetAstNodeInfo(rootAstNodeLink);
119+
120+
// Assert
121+
Assert.Contains("NodeType", nodeInfo.Keys);
122+
Assert.Contains("Text", nodeInfo.Keys);
123+
Assert.Contains("LinkAddress", nodeInfo.Keys);
124+
Assert.Contains("HasPositionInfo", nodeInfo.Keys);
125+
Assert.False(nodeInfo.ContainsKey("Error"));
126+
}
127+
128+
[Fact]
129+
public void GetAllAstNodes_AfterTransformation_ShouldReturnStoredNodes()
130+
{
131+
// Arrange
132+
var code1 = "public class Class1 {}";
133+
var code2 = "public interface ITest {}";
134+
135+
// Act
136+
_storage.TransformCodeToAst(code1);
137+
_storage.TransformCodeToAst(code2);
138+
var allAstNodes = _storage.GetAllAstNodes();
139+
140+
// Assert
141+
Assert.True(allAstNodes.Count >= 2); // At least 2 root nodes
142+
}
143+
144+
public void Dispose()
145+
{
146+
_storage?.Dispose();
147+
if (System.IO.File.Exists(_testDbPath))
148+
{
149+
System.IO.File.Delete(_testDbPath);
150+
}
151+
}
152+
}
153+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using System;
2+
using System.IO;
3+
using Storage.Local;
4+
using Storage.AST;
5+
6+
namespace Storage.Tests
7+
{
8+
/// <summary>
9+
/// Example demonstrating how to use the AST transformation functionality.
10+
/// </summary>
11+
public class AstUsageExample
12+
{
13+
public static void RunExample()
14+
{
15+
var dbPath = Path.GetTempFileName();
16+
try
17+
{
18+
using var storage = new FileStorage(dbPath);
19+
20+
// Example C# code to transform into AST
21+
var csharpCode = @"using System;
22+
namespace MyNamespace
23+
{
24+
public class Calculator
25+
{
26+
private int result;
27+
28+
public int Add(int a, int b)
29+
{
30+
result = a + b;
31+
return result;
32+
}
33+
34+
public void Reset()
35+
{
36+
result = 0;
37+
}
38+
}
39+
}";
40+
41+
Console.WriteLine("Original C# code:");
42+
Console.WriteLine(csharpCode);
43+
Console.WriteLine("\n" + new string('=', 50) + "\n");
44+
45+
// Transform code into AST and store it in links
46+
var rootAstNodeLink = storage.TransformCodeToAst(csharpCode);
47+
Console.WriteLine($"Root AST node stored at link address: {rootAstNodeLink}");
48+
49+
// Get all AST nodes from the links store
50+
var allAstNodes = storage.GetAllAstNodes();
51+
Console.WriteLine($"Total AST nodes stored: {allAstNodes.Count}");
52+
53+
// Display information for each AST node
54+
Console.WriteLine("\nAST Nodes with position mapping:");
55+
Console.WriteLine(new string('-', 60));
56+
57+
foreach (var nodeLink in allAstNodes)
58+
{
59+
var nodeInfo = storage.GetAstNodeInfo(nodeLink);
60+
if (!nodeInfo.ContainsKey("Error"))
61+
{
62+
Console.WriteLine($"Link: {nodeLink}");
63+
Console.WriteLine($"Type: {nodeInfo["NodeType"]}");
64+
Console.WriteLine($"Text: \"{nodeInfo["Text"]}\"");
65+
Console.WriteLine($"Has Position Info: {nodeInfo["HasPositionInfo"]}");
66+
Console.WriteLine();
67+
}
68+
}
69+
70+
// Demonstrate direct AST transformer usage
71+
var transformer = new CSharpAstTransformer();
72+
var astRoot = transformer.TransformCode(csharpCode);
73+
74+
Console.WriteLine("\nDirect AST Transformation Result:");
75+
Console.WriteLine(new string('-', 40));
76+
PrintAstNode(astRoot, 0);
77+
78+
// Get all nodes with positions
79+
var allNodes = transformer.GetAllNodesWithPositions(astRoot);
80+
Console.WriteLine($"\nTotal nodes with position mapping: {allNodes.Count}");
81+
Console.WriteLine("\nNode position details:");
82+
Console.WriteLine(new string('-', 80));
83+
84+
foreach (var node in allNodes.Take(10)) // Show first 10 for brevity
85+
{
86+
Console.WriteLine($"{node.NodeType,-20} | Pos: {node.StartPosition,3}-{node.EndPosition,3} | Line: {node.StartLine,2}-{node.EndLine,2} | \"{node.Text.Replace("\n", "\\n").Trim()}\"");
87+
}
88+
89+
if (allNodes.Count > 10)
90+
{
91+
Console.WriteLine($"... and {allNodes.Count - 10} more nodes");
92+
}
93+
}
94+
finally
95+
{
96+
if (System.IO.File.Exists(dbPath))
97+
{
98+
System.IO.File.Delete(dbPath);
99+
}
100+
}
101+
}
102+
103+
private static void PrintAstNode(AstNode node, int depth)
104+
{
105+
var indent = new string(' ', depth * 2);
106+
var truncatedText = node.Text.Length > 30 ? node.Text.Substring(0, 30) + "..." : node.Text;
107+
truncatedText = truncatedText.Replace("\n", "\\n").Replace("\r", "\\r");
108+
109+
Console.WriteLine($"{indent}{node.NodeType} [{node.StartLine}:{node.StartColumn}-{node.EndLine}:{node.EndColumn}] \"{truncatedText}\"");
110+
111+
foreach (var child in node.Children)
112+
{
113+
PrintAstNode(child, depth + 1);
114+
}
115+
}
116+
}
117+
}
118+
119+
// Extension method to provide Take functionality
120+
public static class EnumerableExtensions
121+
{
122+
public static System.Collections.Generic.IEnumerable<T> Take<T>(this System.Collections.Generic.IEnumerable<T> source, int count)
123+
{
124+
var enumerator = source.GetEnumerator();
125+
int current = 0;
126+
while (current < count && enumerator.MoveNext())
127+
{
128+
yield return enumerator.Current;
129+
current++;
130+
}
131+
}
132+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8</TargetFramework>
5+
<IsPackable>false</IsPackable>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
10+
<PackageReference Include="xunit" Version="2.6.3" />
11+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
12+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
13+
<PrivateAssets>all</PrivateAssets>
14+
</PackageReference>
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="../Storage/Storage.csproj" />
19+
</ItemGroup>
20+
21+
</Project>

0 commit comments

Comments
 (0)