I need to parse fragments of user-written C# code and replace all variables that aren't defined locally with method calls. I.e.
public class Foo
{
public dynamic Bar()
{
return Math.Min(x + width, maxWidth);
}
}
has to become:
public class Foo
{
public dynamic Bar()
{
return Math.Min(Resolve("x") + Resolve("width"), Resolve("maxWidth"));
}
}
I'm using Microsoft.CodeAnalysis.CSharp and the CSharpSyntaxTree to examine the string, but it doesn't give me enough information to perform the replace. Or if it does, I don't know where to look for it. I've pasted the SyntaxTree layout below. All the variables occur as IdentifierName nodes, but I don't know how to tell different IdentifierNames apart. Where to go from here?
CompilationUnit[0..99) {
code: public class Foo\n{\n public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n}
tokens: EndOfFileToken[]
nodes{
ClassDeclaration[0..99) {
code: public class Foo\n{\n public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n}
tokens: PublicKeyword[public ] ClassKeyword[class ] IdentifierToken[Foo\n] OpenBraceToken[{\n] CloseBraceToken[}]
nodes{
MethodDeclaration[21..98) {
code: public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n
tokens: PublicKeyword[ public ] IdentifierToken[Bar]
nodes{
IdentifierName[30..38) {
code: dynamic
tokens: IdentifierToken[dynamic ]
}
ParameterList[41..45) {
code: ()\n
tokens: OpenParenToken[(] CloseParenToken[)\n]
}
Block[45..98) {
code: {\n return Math.Min(x + width, maxWidth);\n }\n
tokens: OpenBraceToken[ {\n] CloseBraceToken[ }\n]
nodes{
ReturnStatement[50..93) {
code: return Math.Min(x + width, maxWidth);\n
tokens: ReturnKeyword[ return ] SemicolonToken[;\n]
nodes{
InvocationExpression[61..90) {
code: Math.Min(x + width, maxWidth)
nodes{
SimpleMemberAccessExpression[61..69) {
code: Math.Min
tokens: DotToken[.]
nodes{
IdentifierName[61..65) {
code: Math
tokens: IdentifierToken[Math]
}
IdentifierName[66..69) {
code: Min
tokens: IdentifierToken[Min]
}
}
}
ArgumentList[69..90) {
code: (x + width, maxWidth)
tokens: OpenParenToken[(] CommaToken[, ] CloseParenToken[)]
nodes{
Argument[70..79) {
code: x + width
nodes{
AddExpression[70..79) {
code: x + width
tokens: PlusToken[+ ]
nodes{
IdentifierName[70..72) {
code: x
tokens: IdentifierToken[x ]
}
IdentifierName[74..79) {
code: width
tokens: IdentifierToken[width]
}
}
}
}
}
Argument[81..89) {
code: maxWidth
nodes{
IdentifierName[81..89) {
code: maxWidth
tokens: IdentifierToken[maxWidth]
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
Place the cursor inside a variable you want to rename and press Ctrl + R . Then type other name and editor will change all occurrences of that variable. Save this answer.
Select Edit > Refactor > Rename. Right-click the code and select Rename.
We can change the value of a variable in C or any other language, and we can also reuse it multiple times.
I think you need to use the semantic model. Here's a (very basic) example that shows how to find unresolved symbols:
var tree = CSharpSyntaxTree.ParseFile(fileName);
var root = tree.GetRoot();
var refs = new MetadataReference[]
{
new MetadataFileReference(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll", new MetadataReferenceProperties(MetadataImageKind.Assembly))
};
var compilation = CSharpCompilation.Create("testRoslyn", new[] { tree }, refs);
var model = compilation.GetSemanticModel(tree);
var unknownSymbols =
from node in root.DescendantNodes()
where node.IsKind(SyntaxKind.IdentifierName)
let symbolInfo = model.GetSymbolInfo(node)
where symbolInfo.Symbol == null && !symbolInfo.CandidateSymbols.Any()
select node;
From there you can replace the nodes with Resolve(name)
.
This post doesn't really offer you a solution to your problem, but instead another way that may be easier to implement, but introduce a change for the user. I post here only as an idea.
The objective is not to allow the user to write x
but instead Var.x
or Var.maxWidth
Then, when parsing your C# code, you just need to insert code for a property Var
of type CustomDynamicObject
(or any name you want to give)
public (static?) CustomDynamicObject Var { get { /* create the object once and return */ }}
Then you can defined CustomDynamicObject
inheriting DynamicObject, so that you can intercept all calls to undefined methods/property
DynamicObject and using dynamic feature of .NET 4 is just a way to intercept call, but you can google for other techniques.
Perhaps you accidentally paste CSharpSyntaxTree of code without declaration "double width = 10.0;"? If so, you get this additional declarations in your CSharpSyntaxTree.
All you need to do is just scan the tree for IdentifierToken`s that not declarated in user code, all this tokens have positions that you must use for replace variables acces code to method call code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With