2013-07-11 2 views
3

나는 C# 소스 코드의 키워드를 정규 표현식과 일치시키고 싶다. "새로운"키워드가 있다고 가정 해 봅시다. 그러나소스 코드의 일치 키워드

\b[^\[email protected]]new\b 

: I "는"내부가 아닌 모든 "새로운"키워드 지금까지 작성한 // (주석) 및/* */(두 번째 주석)

를 일치시킬 그것은을 위해 작동하지 않습니다 :

new[] 
var a = new[] { "bla" }; 
var string = "new" 
foo(); // new 
/* new */ 

어떻게 그 정규식을 향상시킬 수 있습니까?

+1

정말로 무엇을하려고합니까? 왜 C# 키워드를 매치해야합니까? –

+13

아마도 Roslyn을 사용해야합니다. 소스 코드를 정규식으로 파싱하는 것은 불만을 해결하는 방법입니다. – SLaks

+0

소스 코드에 대한 강조 표시 (HTML로)를 만들고 싶습니다. 요소로 키워드를 꾸밀 필요가 있습니다. 나는 완벽한 알고리즘을 만들고 싶지는 않지만 직접 (교육 목적으로) 만들려고한다. –

답변

1

간단한 출력하는 lexer를 사용합니다. 렉서는 문자열에있는 텍스트 그룹을 찾아 해당 그룹에서 토큰을 생성합니다. 그런 다음 토큰에 "유형"이 제공됩니다. (무엇인지 정의하는 것)

C# 키워드는 정의 된 C# keywords 중 하나입니다. 이 간단한 정규식은 가능한 C# 키워드 중 하나가 오는 테두리를 정의합니다. ("\b(new|var|string|...)\b")

귀하의 렉서는 각 경기에 대한 토큰을 생성, 키워드 주어진 문자열에서 모든 일치를 발견하고 토큰 "type""keyword"라고 말할 것입니다.

그러나 당신이 말한 것처럼, 따옴표 나 주석으로 키워드를 찾으려는 것은 아닙니다. 이것은 렉서가 실제로 점수를 얻는 곳입니다.

  1. 다른 경기에 포함 된 모든 일치 항목을 제거합니다

    은 (정규식 기반) 렉서는 두 가지 방법을 사용하는 것이이 사건을 해결하려면.
  2. 다른 공간과 동일한 공간을 사용하지만 우선 순위가 낮은 인 일치 항목을 제거하십시오.

렉서는 다음과 같이 작동합니다

  1. 의 각을 통해 인덱스
  2. 루프에 의해
  3. 주문을 토큰 토큰 그들을 변환] 정규 표현식에에서 경기를 모두 찾기 다음 일치 항목이이 일치 항목에 부분적으로 포함되어 있으면 다음 일치 항목 인 과 일치하는 토큰 (또는 둘 모두가 같은 공간) 그것을 제거하십시오.

스포일러 경고 다음은 완전한 기능 렉서입니다. 그것은 완전히 기능적인 렉서이기 때문에 렉서가 어떻게 작동 하는지를 보여줄 것입니다. 예를 들어

: 문자열, 주석 및 키워드에 대한

을 감안할 때 정규 표현식에는 렉서가 그들 사이의 충돌을 해결하는 방법을 보여줍니다. (나는 그것을 테스트) 유효한 거의 완성 렉서. 그것은 그들을 혼동 당신이 정규식에서 정의하는 모든 키워드를 찾을 수 없습니다 (그것을 사용하거나 직접 작성 부담)입니다

//Simple Regex for strings 
string StringRegex = "\"(?:[^\"\\\\]|\\\\.)*\""; 

//Simple Regex for comments 
string CommentRegex = @"//.*|/\*[\s\S]*\*/"; 

//Simple Regex for keywords 
string KeywordRegex = @"\b(?:new|var|string)\b"; 

//Create a dictionary relating token types to regexes 
Dictionary<string, string> Regexes = new Dictionary<string, string>() 
{ 
    {"String", StringRegex}, 
    {"Comment", CommentRegex}, 
    {"Keyword", KeywordRegex} 
}; 

//Define a string to tokenize 
string input = "string myString = \"Hi! this is my new string!\"//Defines a new string."; 


//Lexer steps: 
//1). Find all of the matches from the regexes 
//2). Convert them to tokens 
//3). Order the tokens by index then priority 
//4). Loop through each of the tokens comparing 
// the current match with the next match, 
// if the next match is partially contained by this match 
// (or if they both occupy the same space) remove it. 


//** Sorry for the complex LINQ expression (not really) ** 

//Match each regex to the input string(Step 1) 
var matches = Regexes.SelectMany(a => Regex.Matches(input, a.Value) 
//Cast each match because MatchCollection does not implement IEnumerable<T> 
.Cast<Match>() 
//Select a new token for each match(Step 2) 
.Select(b => 
     new 
     { 
      Index = b.Index, 
      Value = b.Value, 
      Type = a.Key //Type is based on the current regex. 
     })) 
//Order each token by the index (Step 3) 
.OrderBy(a => a.Index).ToList(); 

//Loop through the tokens(Step 4) 
for (int i = 0; i < matches.Count; i++) 
{ 
    //Compare the current token with the next token to see if it is contained 
    if (i + 1 < matches.Count) 
    { 
     int firstEndPos = (matches[i].Index + matches[i].Value.Length); 
     if (firstEndPos > matches[(i + 1)].Index) 
     { 
      //Remove the next token from the list and stay at 
      //the current match 
      matches.RemoveAt(i + 1); 
      i--; 
     } 
    } 
} 

//Now matches contains all of the right matches 
//Filter the matches by the Type to single out keywords from comments and 
//string literals. 
foreach(var match in matches) 
{ 
    Console.WriteLine(match); 
} 
Console.ReadLine(); 

문자열 리터럴 또는 주석.

+0

정규식을 사용하면 시행 착오보다 더 잘 할 수 있습니다. 코드에서 암시 적 또는 명시 적 상태 시스템을 만들거나 명명 된 캡처 그룹에서 여러 정규 옵션을 사용하는 단일 정규식을 사용할 수 있습니다 (수작업으로 작성하지 않고 코드로 함께 묶음). – siride

+0

예, 알고 있습니다. 그러나 샘플에 추가 할 복잡성이 학습 곡선을 증가시킬 것이라고 생각했습니다. 그러므로 최적화 없이는 학습 자료가 더 좋을 것입니다. – AtinSkrita

+0

귀하의 답변은 실제 자료를 제공하는 데 적합합니다. 나는 assambly 하이라이트 소스 코드를 만들었습니다. (언어/구문을 추가하려면 적절한 인터페이스 만 구현하면됩니다.) 효율적인 솔루션을 만드는 것은 좋은 기반이라고 생각합니다. –

2

설명

그것은 모든 바람직하지 않은 일치 모든 좋은 일들을 캡처하는 것이 더 쉽습니다. 그런 다음 나중에 프로그래밍 로직 테스트에서 캡처 그룹이 채워 졌는지 확인하고, 그렇다면 원하는 일치 항목을 찾습니다.

이 식은 것입니다 :

  • 피하기 "new" 또는 'new'
  • 피할 등의 모든 텍스트 단일 및 이중 인용 블록 모든 블록은 모두 한 줄 // new
  • 하나를 /* new */
  • 피하기 같은 부분을 코멘트의 주석 부분 new, varfoo
  • 와 같이 인용되지 않거나 댓글이 달린 키워드

    enter image description here

    (\/\*(?:(?!\*\/)|.)*\*\/|\/{2}[^\r\n]*[\r\n]+)|("[^"]*"|'[^']*')|(new|var|foo)|(\w+)

나는 C#을 모르는, 그래서 나는 이러한 목표를 달성 할 방법을 설명하기 위해 파워 쉘 예제를 제공합니다. 나는 표현식을 insensitve로 만들고 (?is)을 사용하여 "dot matches new lines"을 켜고 표현의 모든 작은 따옴표를 ''으로 이스케이프해야했습니다.

코드

$String = 'NEW[] 
var a = NEw[] { "bla" }; 
var string = "new" 
foo(); // new 
/* 
new 
*/ 
' 
clear 

[regex]$Regex = '(?is)(\/\*(?:(?!\*\/)|.)*\*\/|\/{2}[^\r\n]*[\r\n]+)|("[^"]*"|''[^'']*'')|(new|var|foo)|(\w+)' 

# cycle through all matches 
$Regex.matches($String) | foreach { 

    # Capture group 1 collects the comments, if populated then this match is a comment 
    if ($_.Groups[1].Value) { 
     Write-Host "comment at " $_.Groups[1].index " with a value => " $_.Groups[1].Value 
     } # end if 

    # capture group 2 collects the quoted strings, if populated then this match is a quoted string 
    if ($_.Groups[2].Value) { 
     Write-Host "quoted string at " $_.Groups[2].index " with a value => " $_.Groups[2].Value 
     } # end if 

    # capture group 3 collects keywords like new, var, and foo, if populated then this match is a keyword 
    if ($_.Groups[3].Value) { 
     Write-Host "keyword at " $_.Groups[3].index " with a value => " $_.Groups[3].Value 
     } # end if 

    # capture group 4 collects all the other word character chunks, so these might be variable names 
    if ($_.Groups[4].Value) { 
     Write-Host "possible variable name at " $_.Groups[4].index " with a value => " $_.Groups[4].Value 
     } # end if 

    } # next match 

keyword at 0 with a value => NEW 
keyword at 7 with a value => var 
possible variable name at 11 with a value => a 
keyword at 15 with a value => NEw 
quoted string at 23 with a value => "bla" 
keyword at 33 with a value => var 
possible variable name at 37 with a value => string 
quoted string at 46 with a value => "new" 
keyword at 53 with a value => foo 
comment at 60 with a value => // new 

comment at 68 with a value => /* 
new 
*/