2014-07-17 10 views
9

사용자 작성 C# 코드 조각을 구문 분석하고 메서드 호출로 로컬에 정의되지 않은 모든 변수를 바꿔야합니다. 나는. C# 코드의 모든 변수를 메서드로 바꿉니다

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

이되고있다 : 나는 문자열을 검사 Microsoft.CodeAnalysis.CSharp과 CSharpSyntaxTree을 사용하고

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

, 그러나 그것은 나에게 대체 수행 할 수있는 충분한 정보를 제공하지 않습니다. 그렇지 않으면 어디서 찾아야할지 모르겠다. 아래 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

실수로 "double width = 10.0;"선언없이 코드의 CSharpSyntaxTree를 붙여 넣을 수 있습니까? 그렇다면 CSharpSyntaxTree에서이 추가 선언을 얻습니다.

사용자 코드에서 선언되지 않은 IdentifierToken`s에 대한 트리 만 검색하면됩니다.이 모든 토큰에는 대체 변수에 사용해야하는 위치가 있으며 메서드 호출 코드에 대한 코드가 들어 있습니다.

+0

실수로 표시 코드를 변경했습니다. 둘 다 정의되지 않은 변수를 정의하여 코드 트리가 유효하지 않게된다는 사실을 잊어 버렸습니다. 나는 그것을 다시 바꿨다. –

2

이 게시물은 실제로 당신에게 당신의 문제에 대한 해결책을 제공하지는 않지만 대신 구현하기가 쉽지만 사용자에게 변화를 가져 오는 또 다른 방법입니다. 나는 아이디어로 여기 게시에만.

목적은 사용자가 C# 코드를 분석 할 때 x 대신 Var.x 또는 다음

Var.maxWidth는, 당신은 단지 형 CustomDynamicObject의 속성 Var (또는 이름 당신을 위해 코드를 삽입해야 쓸 수하지 않는 것입니다

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

그런 다음 당신은 당신이 정의되지 않은 방법/부동산

에 대한 모든 호출을 가로 챌 수 있도록 CustomDynamicObjectDynamicObject을 상속 정의 할 수 있습니다)주고 싶어 0

DynamicObject 및 .NET 4의 동적 기능을 사용하는 것은 호출을 가로 채기위한 방법 일 뿐이지 만 다른 기술에 대해서는 google을 사용할 수 있습니다.

+0

도움을받을 수 있다면 사용자에게 어떤 책임도지지 않을 것입니다. 흥미로운 아이디어이지만 초기에는 사용자 코드를 일반 C# 코드와 다르게해야하는 문제를 해결하고 싶습니다. –

+0

네, 그것이 "아이디어로"글을 올리는 이유입니다. 문제에 대한 해결책을 찾지 못하면 백업 솔루션이 될 수 있습니다. 하지만 사용자에게 가장 적은 스트레스가 있어야합니다. – Fabske