나는 학위를 마치기 위해 천천히 노력하고 있으며 이번 학기는 Compilers 101입니다. 우리는 Dragon Book을 사용 하고 있습니다. 과정을 시작하면서 어휘 분석과 결정 론적 유한 오토마타 (이하 DFA)를 통해이를 분석하는 방법에 대해 이야기합니다. 다양한 렉서 상태를 설정하고 그 사이의 전환을 정의하십시오.
그러나 교수와 책은 거대한 2 차원 배열 (1 차원의 다양한 비 터미널 상태 및 다른 차원의 가능한 입력 기호)에 해당하는 전이 테이블과 모든 터미널을 처리하는 스위치 문을 통해 구현하는 것을 제안합니다 비 터미널 상태 인 경우 전이 테이블로 디스패치 할 수 있습니다.
이론은 모두 훌륭하고 훌륭하지만 실제로 수십 년 동안 코드를 작성한 사람은 구현이 적습니다. 테스트 할 수없고, 유지 관리 할 수없고, 읽을 수 없으며, 디버깅하는 데 어려움이 있습니다. 더 나쁜 것은, 언어가 UTF를 지원한다면 어떻게 원격으로 실용적인지 알 수 없습니다. 비 터미널 상태마다 백만 개 정도의 전이 테이블 항목이 있으면 서두르지 않습니다.
그래서 거래는 무엇입니까? 왜 주제에 관한 결정적인 책이 이런 식으로 말하고 있습니까?
함수 호출의 오버 헤드가 실제로 그렇게 많은가? 문법이 미리 알려지지 않았을 때 (정규 표현) 잘 작동하거나 필요한 것입니까? 또는 더 구체적인 솔루션이 더 구체적인 문법에 더 효과적 일지라도 모든 경우를 처리하는 것입니까?
( 참고 : 가능한 중복 " 거대한 switch 문 대신 OO 방식을 사용하는 이유는 무엇입니까? "는 가깝지만 OO에 대해서는 신경 쓰지 않습니다. 독립형 함수를 사용하는 기능적 접근 방식이나 더 정성적인 명령 방식도 좋습니다.)
예를 들어, 식별자 만있는 언어를 고려하십시오 [a-zA-Z]+
. 이러한 식별자는 입니다. DFA 구현에서는 다음과 같은 결과가 나타납니다.
private enum State
{
Error = -1,
Start = 0,
IdentifierInProgress = 1,
IdentifierDone = 2
}
private static State[][] transition = new State[][]{
///* Start */ new State[]{ State.Error, State.Error (repeat until 'A'), State.IdentifierInProgress, ...
///* IdentifierInProgress */ new State[]{ State.IdentifierDone, State.IdentifierDone (repeat until 'A'), State.IdentifierInProgress, ...
///* etc. */
};
public static string NextToken(string input, int startIndex)
{
State currentState = State.Start;
int currentIndex = startIndex;
while (currentIndex < input.Length)
{
switch (currentState)
{
case State.Error:
// Whatever, example
throw new NotImplementedException();
case State.IdentifierDone:
return input.Substring(startIndex, currentIndex - startIndex);
default:
currentState = transition[(int)currentState][input[currentIndex]];
currentIndex++;
break;
}
}
return String.Empty;
}
(파일 끝을 올바르게 처리하는 것이지만)
내가 기대하는 것과 비교하여 :
public static string NextToken(string input, int startIndex)
{
int currentIndex = startIndex;
while (currentIndex < startIndex && IsLetter(input[currentIndex]))
{
currentIndex++;
}
return input.Substring(startIndex, currentIndex - startIndex);
}
public static bool IsLetter(char c)
{
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
}
NextToken
DFA 시작부터 여러 대상이 있으면 코드가 자체 기능으로 리팩토링됩니다.