이해하기 위해 파서는 파싱 트리를 만든 다음 그 트리를 버립니다. 그러나 컴파일러가 사용하는 추상 구문 트리를 표시 할 수도 있습니다.
구문 분석 트리와 추상 구문 트리가 구문 분석 단계에서 생성된다는 인상을 받고 있습니다. 그렇다면 누군가 이것이 왜 다른지 설명 할 수 있습니까?
이해하기 위해 파서는 파싱 트리를 만든 다음 그 트리를 버립니다. 그러나 컴파일러가 사용하는 추상 구문 트리를 표시 할 수도 있습니다.
구문 분석 트리와 추상 구문 트리가 구문 분석 단계에서 생성된다는 인상을 받고 있습니다. 그렇다면 누군가 이것이 왜 다른지 설명 할 수 있습니까?
답변:
구문 분석 트리는 구체적 구문 트리라고도합니다.
기본적으로 초록 나무는 콘크리트 나무보다 정보가 적습니다. 콘크리트 트리에는 언어의 각 요소가 포함되어 있으며 추상 트리는 흥미없는 조각을 버렸습니다.
예를 들면 다음과 같습니다. (2 + 5) * 8
콘크리트는 다음과 같습니다
( 2 + 5 ) * 8
| \ | / | | |
| \|/ | | |
\___|__/ | |
\______|/
추상 트리에는 다음이 있습니다.
2 5
\/
+ 8
\/
*
구체적인 경우에는 괄호와 모든 언어가 트리에 통합되었습니다. 추상적 인 경우에는 정보가 트리 구조에 통합 되었기 때문에 괄호는 사라졌습니다.
가장 먼저 이해해야 할 것은 아무도 파서 나 컴파일러를 특정 방식으로 작성하도록 강요하지 않는다는 것입니다. 특히 파서의 결과가 반드시 트리 여야하는 것은 아닙니다. 입력을 나타내는 데 적합한 데이터 구조 일 수 있습니다.
예를 들어 다음 언어는
prog:
definition
| definition ';' prog
;
definition: .....
정의 목록으로 표시 될 수 있습니다. (Nitpickers는 목록 이 퇴화 된 트리이지만 어쨌든 지적 합니다.)
둘째, 구문 분석 트리 (또는 구문 분석기가 리턴 한 데이터 구조)를 유지할 필요가 없습니다. 반대로, 컴파일러는 일반적으로 이전 패스의 결과를 변환하는 일련의 패스로 구성됩니다. 따라서 컴파일러의 전체 레이아웃은 다음과 같습니다.
parser :: String -> Maybe [Definitions] -- parser
pass1 :: [Definitions] -> Maybe DesugaredProg -- desugarer
pass2 :: DesugaredProg -> Maybe TypedProg -- type checker
pass3 :: TypedProg -> Maybe AbstractTargetLang -- code generation
pass4 :: AbstractTargetLang -> Maybe String -- pretty printer
compiler :: String -> Maybe String -- transform source code to target code
compiler source = do
defs <- parser source
desug <- pass1 defs
typed <- pass2 desug
targt <- pass3 typed
pass4 targt
결론 : 사람들이 구문 분석 트리 , 추상 구문 트리 , 구체적인 구문 트리 등에 대해 이야기하는 것을 들으면 항상 주어진 목적에 적합한 데이터 구조로 대체 하면 좋습니다.