2014-07-17 88 views
9

我需要解析用戶編寫的C#代碼片段,並用方法調用替換本地未定義的所有變量。即用方法替換C#代碼中的所有變量

public class Foo 
{ 
    public dynamic Bar() 
    { 
    return Math.Min(x + width, maxWidth); 
    } 
} 

有可能成爲:

public class Foo 
{ 
    public dynamic Bar() 
    { 
     return Math.Min(Resolve("x") + Resolve("width"), Resolve("maxWidth")); 
    } 
} 

我使用Microsoft.CodeAnalysis.CSharp和CSharpSyntaxTree檢查字符串,但它並沒有給我足夠的信息來進行替換。或者如果是這樣,我不知道在哪裏尋找它。我粘貼了下面的SyntaxTree佈局。所有變量都作爲IdentifierName節點出現,但我不知道如何區分不同的IdentifierNames。然後去哪兒?

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] 
       } 
       } 
       } 
      } 
      } 
      } 
      } 
     } 
     } 
     } 
     } 
    } 
    } 
    } 
    } 
} 
} 
+1

語法樹不夠,您需要查看語義模型以查看標識符代表的內容。 –

+0

你能提供一個鏈接,我可以去閱讀語義模型嗎? –

+0

現在正在尋找...我有一段時間沒有看過Roslyn,並且自上次以來API已經發生了很大的變化 –

回答

7

我認爲你需要使用語義模型。這裏有一個(非常基本的)示例,說明如何找到未解決的符號:

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; 

從那裏,你可以用Resolve(name)更換節點。

+0

那麼,這一切都工作得很好,我仍然完全堅持如何替換節點。有一些關於Microsoft.CodeAnalysis背後的邏輯,我只是​​還沒有得到... –

+1

你有沒有讀過FAQ?他們討論替換那裏的子表達式:https://roslyn.codeplex.com/wikipage?title=FAQ&referringTitle=Documentation – JoshVarty

+0

我似乎無法下載應該包含示例的SDK預覽版。該鏈接將我帶到https://connect.microsoft.com/VisualStudio,而這一切都是從那裏開始的。我在電腦上搜索了faq.cs和其他所有我能想到的東西,但沒有喜悅。 –

1

也許你不經意地粘貼CSharpSyntaxTree而不聲明「double width = 10.0;」? 如果是這樣,您將在CSharpSyntaxTree中獲得這些額外的聲明。

您只需掃描未在用戶代碼中聲明的IdentifierToken的樹,所有這些標記都具有必須使用的位置,以將變量acces代碼替換爲方法調用代碼。

+0

我錯誤地更改了代碼,以後顯示都定義了一個未定義的變量,忘記了代碼樹會變得無效。我改回來了。 –

2

本文並不真正爲您解決問題,而是另一種可能更容易實施的方式,但爲用戶引入更改。我只是在這裏發表一個想法。

的目的不是爲了讓用戶編寫x而是Var.xVar.maxWidth

然後,解析您的C#代碼時,你只需要爲CustomDynamicObject類型的屬性Var(或你的任何名稱插入代碼想給)

public (static?) CustomDynamicObject Var { get { /* create the object once and return */ }} 

然後你就可以定義CustomDynamicObject繼承DynamicObject,這樣就可以攔截到未定義的方法/屬性

所有呼叫0

DynamicObject和使用.NET 4的動態特性只是攔截調用的一種方式,但您可以使用谷歌的其他技術。

+0

如果我能幫到的話,我寧願不對用戶承擔任何責任。這是一個有趣的想法,但最初我想解決它,而不需要用戶代碼與常規C#代碼不同。 –

+0

是的,這就是爲什麼我發佈它「作爲一個想法」。如果您沒有找到解決問題的辦法,那可能是一個備份解決方案。但你完全正確,用戶必須承受最小的壓力。 – Fabske