다음을 통해 공유


구문 작업

구문 트리는 컴파일러 API에 의해 노출되는 기본적인 변경할 수 없는 데이터 구조입니다. 이러한 트리는 소스 코드의 어휘 및 구문 구조를 나타냅니다. 두 가지 중요한 목적을 제공합니다.

  • IDE, 추가 기능, 코드 분석 도구 및 리팩터링과 같은 도구가 사용자 프로젝트에서 소스 코드의 구문 구조를 보고 처리하도록 허용합니다.
  • 리팩터링 및 IDE와 같은 도구를 사용하도록 설정하여 직접 텍스트 편집을 사용하지 않고도 자연스럽게 소스 코드를 만들고 수정하고 다시 정렬할 수 있습니다. 도구를 사용하면 트리를 만들고 조작하여 소스 코드를 쉽게 만들고 다시 정렬할 수 있습니다.

구문 트리

구문 트리는 컴파일, 코드 분석, 바인딩, 리팩터링, IDE 기능 및 코드 생성에 사용되는 기본 구조입니다. 소스 코드의 어떤 부분도 먼저 식별되고 잘 알려진 많은 구조적 언어 요소 중 하나로 분류되지 않고 이해되지 않습니다.

비고

RoslynQuoter 는 프로그램의 구문 트리를 생성하는 데 사용되는 구문 팩터리 API 호출을 보여 주는 오픈 소스 도구입니다. 라이브로 사용해 보려면 다음을 참조하세요 http://roslynquoter.azurewebsites.net.

구문 트리에는 세 가지 주요 특성이 있습니다.

  • 모든 원본 정보를 충실하게 유지합니다. 전체 충실도는 구문 트리가 원본 텍스트에 있는 모든 정보, 모든 문법 구문, 모든 어휘 토큰 및 공백, 주석 및 전처리기 지시문을 포함하여 그 사이에 있는 모든 정보를 포함함을 의미합니다. 예를 들어 원본에 언급된 각 리터럴은 입력된 것과 정확하게 표시됩니다. 또한 구문 트리는 건너뛰거나 누락된 토큰을 표시하여 프로그램이 불완전하거나 형식이 잘못된 경우 소스 코드에서 오류를 캡처합니다.
  • 그들은 구문 분석된 내용에서 정확한 텍스트를 생성할 수 있습니다. 구문 노드에서 해당 노드에 루팅된 하위 트리의 텍스트 표현을 가져올 수 있습니다. 이 기능은 구문 트리를 소스 텍스트를 생성하고 편집하는 방법으로 사용할 수 있음을 의미합니다. 트리를 만듦으로써 해당 텍스트를 의미상 생성한 것이 되고, 기존 트리에 변경을 가하여 새 트리를 만들면 텍스트를 효과적으로 편집한 것이 됩니다.
  • 변경할 수 없으며 스레드로부터 안전합니다. 트리를 가져온 후에는 코드의 현재 상태에 대한 스냅샷이며 변경되지 않습니다. 이렇게 하면 여러 사용자가 잠금 또는 중복 없이 여러 스레드에서 동일한 구문 트리와 동시에 상호 작용할 수 있습니다. 트리는 변경할 수 없으며 트리에 직접 수정할 수 없으므로 팩터리 메서드는 트리의 추가 스냅샷을 만들어 구문 트리를 만들고 수정하는 데 도움이 됩니다. 트리는 기본 노드를 다시 사용하는 방식에서 효율적이므로 새 버전을 빠르고 추가 메모리 없이 다시 작성할 수 있습니다.

구문 트리는 말 그대로 트리 데이터 구조이며, 터미널이 아닌 구조 요소는 다른 요소와 부모입니다. 각 구문 트리는 노드, 토큰 및 퀴즈로 구성됩니다.

구문 노드

구문 노드는 구문 트리의 기본 요소 중 하나입니다. 이러한 노드는 선언, 문, 절 및 식과 같은 구문 구조를 나타냅니다. 구문 노드의 각 범주는 에서 Microsoft.CodeAnalysis.SyntaxNode파생된 별도의 클래스로 표시됩니다. 노드 클래스 집합은 확장할 수 없습니다.

모든 구문 노드는 구문 트리의 비터미널 노드입니다. 이는 항상 다른 노드와 토큰을 자식으로 가진다는 것을 의미합니다. 다른 노드의 자식인 각 노드에는 속성을 통해 액세스할 수 있는 부모 노드가 SyntaxNode.Parent 있습니다. 노드와 트리는 변경할 수 없으므로 노드의 부모는 변경되지 않습니다. 트리의 루트에는 null 부모가 있습니다.

각 노드에는 SyntaxNode.ChildNodes() 원본 텍스트의 위치에 따라 순차적으로 자식 노드 목록을 반환하는 메서드가 있습니다. 이 목록에는 토큰이 포함되어 있지 않습니다. 또한 각 노드에는 해당 노드의 후손을 검사할 수 있는 메서드가 있습니다. 이러한 메서드는 해당 노드에 의해 루팅된 하위 트리에 존재하는 모든 노드, 토큰 또는 부가 정보 목록을 나타냅니다(예: DescendantNodes, DescendantTokens, 또는 DescendantTrivia).

또한 각 구문 노드 하위 클래스는 강력한 형식의 속성을 통해 동일한 자식을 모두 노출합니다. 예를 들어 BinaryExpressionSyntax 노드 클래스에는 이진 연산자와 관련된 세 가지 Left추가 속성이 OperatorTokenRight있습니다. LeftRight의 형식은 ExpressionSyntax이며, OperatorToken의 형식은 SyntaxToken입니다.

일부 구문 노드에는 선택적인 자식 노드가 있습니다. 예를 들어, IfStatementSyntax에는 선택적 ElseClauseSyntax가 있습니다. 자식이 존재하지 않으면 속성은 null을 반환합니다.

구문 토큰

구문 토큰은 코드의 가장 작은 구문 조각을 나타내는 언어 문법의 터미널입니다. 다른 노드 또는 토큰의 부모는 절대로 없습니다. 구문 토큰은 키워드, 식별자, 리터럴 및 문장 부호로 구성됩니다.

효율성을 위해 형식은 SyntaxToken CLR 값 형식입니다. 따라서 구문 노드와 달리 표현되는 토큰의 종류에 따라 의미가 있는 속성이 혼합된 모든 종류의 토큰에 대한 구조는 하나뿐입니다.

예를 들어 정수 리터럴 토큰은 숫자 값을 나타냅니다. 토큰 범위의 원시 원본 텍스트 외에도 리터럴 토큰에는 정확한 디코딩된 정수 값을 알려주는 Value 속성이 있습니다. 이 속성은 여러 기본 형식 중 하나일 수 있으므로 형식 Object 화됩니다.

속성은 ValueText 속성과 동일한 정보를 Value 제공합니다. 그러나 이 속성은 항상 String로 입력됩니다. C# 원본 텍스트의 식별자에 유니코드 이스케이프 문자가 포함될 수 있지만 이스케이프 시퀀스 자체의 구문은 식별자 이름의 일부로 간주되지 않습니다. 따라서 토큰으로 확장된 원시 텍스트에는 이스케이프 시퀀스가 포함되지만 속성은 ValueText 포함되지 않습니다. 대신 이스케이프로 식별되는 유니코드 문자가 포함됩니다. 예를 들어 원본 텍스트에 \u03C0으로 작성된 식별자가 포함되어 있으면, 이 토큰의 ValueText 속성은 π을 반환합니다.

구문 퀴즈

구문 퀴즈는 공백, 주석 및 전처리기 지시문과 같은 코드의 일반적인 이해를 위해 크게 중요하지 않은 소스 텍스트 부분을 나타냅니다. 구문 토큰과 마찬가지로 퀴즈는 값 형식입니다. 단일 Microsoft.CodeAnalysis.SyntaxTrivia 형식은 모든 종류의 퀴즈를 설명하는 데 사용됩니다.

퀴즈는 일반 언어 구문의 일부가 아니며 두 토큰 사이에 아무 곳이나 나타날 수 있으므로 노드의 자식으로 구문 트리에 포함되지 않습니다. 그러나 리팩터링과 같은 기능을 구현하고 원본 텍스트로 충실도를 유지하는 것이 중요하기 때문에 구문 트리의 일부로 존재합니다.

토큰 SyntaxToken.LeadingTrivia 또는 SyntaxToken.TrailingTrivia 컬렉션을 검사하여 퀴즈에 액세스할 수 있습니다. 원본 텍스트를 구문 분석하면 퀴즈 시퀀스가 토큰과 연결됩니다. 일반적으로 토큰은 다음 토큰까지 동일한 줄에 있는 이후의 모든 사소한 내용을 소유합니다. 해당 줄 뒤의 모든 퀴즈는 다음 토큰과 연결됩니다. 원본 파일의 첫 번째 토큰은 모든 초기 퀴즈를 가져오고 파일의 마지막 퀴즈 시퀀스는 파일 끝 토큰에 압정되며, 그렇지 않으면 너비가 0입니다.

구문 노드 및 토큰과 달리 구문 퀴즈에는 부모가 없습니다. 그러나 트리의 일부이며 각각이 단일 토큰과 연결되어 있으므로 속성을 사용하여 SyntaxTrivia.Token 연결된 토큰에 액세스할 수 있습니다.

범위

각 노드, 토큰 또는 퀴즈는 원본 텍스트 내의 위치와 구성되는 문자 수를 알고 있습니다. 텍스트 위치는 0부터 시작하는 char 인덱스인 32비트 정수로 표시됩니다. TextSpan 개체는 시작 위치와 문자 수이며 둘 다 정수로 표시됩니다. 길이가 0인 경우 TextSpan 두 문자 사이의 위치를 나타냅니다.

각 노드에는 다음과 같은 두 가지 TextSpan 속성 Span 이 있습니다 FullSpan.

이 속성은 Span 노드 하위 트리에서 첫 번째 토큰의 시작부터 마지막 토큰의 끝까지의 텍스트 범위입니다. 이 범위에는 선행 또는 후행 퀴즈가 포함되지 않습니다.

이 속성은 FullSpan 노드의 일반 범위와 선행 또는 후행 퀴즈의 범위를 포함하는 텍스트 범위입니다.

다음은 그 예입니다.

      if (x > 3)
      {
||        // this is bad
          |throw new Exception("Not right.");|  // better exception?||
      }

블록 내의 문장 노드는 단일 세로 막대 (|)로 표시된 범위를 가지고 있습니다. 여기에는 문자 throw new Exception("Not right.");가 포함됩니다. 전체 범위는 이중 세로 막대(||)로 표시됩니다. 여기에는 범위와 동일한 문자와 선행 및 후행 퀴즈와 연결된 문자가 포함됩니다.

종류

각 노드, 토큰 또는 부수적 요소는 SyntaxNode.RawKind 표시되는 정확한 구문 요소를 식별하는 타입 System.Int32의 속성을 가지고 있습니다. 이 값은 언어별 열거형으로 캐스팅할 수 있습니다. 각 언어인 C# 또는 Visual Basic에는 문법의 가능한 모든 노드, 토큰 및 퀴즈 요소를 나열하는 단일 SyntaxKind 열거형(Microsoft.CodeAnalysis.CSharp.SyntaxKindMicrosoft.CodeAnalysis.VisualBasic.SyntaxKind및 각각)이 있습니다. 이 변환은 CSharpExtensions.Kind 또는 VisualBasicExtensions.Kind 확장 메서드에 액세스하여 자동으로 수행할 수 있습니다.

RawKind 속성을 사용하면 동일한 노드 클래스를 공유하는 구문 노드 형식을 쉽게 구분할 수 있습니다. 토큰 및 퀴즈의 경우 이 속성은 한 유형의 요소를 다른 요소와 구분하는 유일한 방법입니다.

예를 들어, 하나의 BinaryExpressionSyntax 클래스는 자식으로 Left, OperatorToken, 및 Right가 있습니다. 이 속성은 Kind 속성인지, AddExpression인지, SubtractExpression인지, 또는 MultiplyExpression 종류의 구문 노드인지를 구분합니다.

팁 (조언)

(C#의 경우) 또는 IsKind (VB의 경우) 확장 메서드를 사용하여 IsKind 종류를 확인하는 것이 좋습니다.

오류

원본 텍스트에 구문 오류가 포함된 경우에도 원본에 왕복할 수 있는 전체 구문 트리가 노출됩니다. 파서가 언어의 정의된 구문을 준수하지 않는 코드를 발견하면 두 가지 기술 중 하나를 사용하여 구문 트리를 만듭니다.

  • 파서가 특정 종류의 토큰을 예상하지만 찾지 못하는 경우 토큰이 예상된 위치의 구문 트리에 누락된 토큰을 삽입할 수 있습니다. 누락된 토큰은 예상한 실제 토큰을 나타내지만 빈 범위가 있으며 해당 속성이 SyntaxNode.IsMissing 반환됩니다 true.

  • 파서는 구문 분석을 계속할 수 있는 토큰을 찾을 때까지 토큰을 건너뛸 수 있습니다. 이 경우 건너뛴 토큰은 종류 SkippedTokensTrivia가 있는 부가 정보 노드로 연결됩니다.