GCC와 LLVM-연타 사용하고있는 것으로 보인다 필기 재귀 하강 파서를 , 그리고 하지 생성, 들소 플렉스를 기반으로, 아래에서 위로 구문 분석 기계.
여기 누군가가 이것이 사실인지 확인해 주시겠습니까? 그렇다면 왜 주류 컴파일러 프레임 워크는 손으로 쓴 파서를 사용합니까?
업데이트 : 여기에이 주제에 대한 흥미로운 블로그
답변
예:
-
GCC는 한때 yacc (bison) 파서를 사용했지만 3.x 시리즈의 어느 시점에서 손으로 쓴 재귀 하강 파서로 대체되었습니다. http://gcc.gnu.org/wiki/New_C_Parser 를 참조하십시오. 관련 패치 제출에 대한 링크.
-
Clang은 또한 손으로 작성한 재귀 하강 파서를 사용합니다 . http://clang.llvm.org/features.html 끝 부분에있는 “C, Objective C, C ++ 및 Objective C ++ 용 단일 통합 파서”섹션을 참조 하십시오 .
답변
C는 구문 분석하기 어렵고 C ++는 본질적으로 불가능하다는 민간 이론이 있습니다.
사실이 아닙니다.
사실은 C와 C ++는 구문 분석 기계를 해킹하고 기호 테이블 데이터를 엉키지 않고 LALR (1) 구문 분석기를 사용하여 구문 분석하기가 매우 어렵다는 것입니다. 실제로 GCC는 YACC와 이와 같은 추가 해커를 사용하여 구문 분석하는 데 사용되었으며, 그렇습니다. 이제 GCC는 손으로 쓴 파서를 사용하지만 여전히 기호 테이블 해커를 사용합니다. Clang 사람들은 자동화 된 파서 생성기를 사용하지 않았습니다. AFAIK Clang 파서는 항상 손으로 코딩 된 재귀 하강입니다.
사실은 C와 C ++가 자동으로 생성 된 강력한 파서 (예 : GLR 파서 ) 로 비교적 쉽게 파싱 할 수 있으며 해킹이 필요하지 않다는 것입니다. 엘사 C ++ 파서는이 하나의 예이다. 우리의 C ++ 프런트 엔드 는 또 다른 것입니다 (모든 “컴파일러”프런트 엔드와 마찬가지로 GLR은 매우 훌륭한 구문 분석 기술입니다).
우리의 C ++ 프론트 엔드는 GCC만큼 빠르지 않고 확실히 Elsa보다 느립니다. 다른 더 긴급한 문제가 있기 때문에 신중하게 튜닝하는 데 약간의 에너지를 쏟았습니다 (수백만 줄의 C ++ 코드에 사용 되었음에도 불구하고). Elsa는 더 일반적이기 때문에 GCC보다 느릴 가능성이 높습니다. 요즘 프로세서 속도를 감안할 때 이러한 차이는 실제로별로 중요하지 않을 수 있습니다.
그러나 오늘날 널리 배포되는 “실제 컴파일러”는 10 년 또는 20 년 전 또는 그 이상의 컴파일러에 뿌리를두고 있습니다. 비 효율성은 훨씬 더 중요했고 아무도 GLR 파서에 대해 들어 본 적이 없었기 때문에 사람들은 자신이 할 줄 아는 일을했습니다. Clang은 확실히 더 최근에 나온 것이지만 민속 정리는 오랫동안 “설득력”을 유지합니다.
더 이상 그렇게 할 필요가 없습니다. 컴파일러 유지 관리 기능이 향상되어 GLR 및 기타 파서를 프런트 엔드로 매우 합리적으로 사용할 수 있습니다.
무엇 이며 사실, 당신의 다정한 이웃 컴파일러의 동작에 알맞은 문법을 얻는 것은 어려운 것입니다. 거의 모든 C ++ 컴파일러가 (대부분의) 원래 표준을 구현하지만 MS 컴파일러의 DLL 사양 등과 같은 다크 코너 확장도 많이있는 경향이 있습니다. 강력한 구문 분석 엔진을 사용하는 경우 구문 분석기 생성기의 한계에 맞추기 위해 문법을 구부리는 대신 현실과 일치하는 최종 문법입니다.
2012 년 11 월 편집 :이 답변을 작성한 이후로 ANSI, GNU 및 MS 변형 방언을 포함한 전체 C ++ 11을 처리하도록 C ++ 프런트 엔드를 개선했습니다. 많은 추가 사항이 있었지만 파싱 엔진을 변경할 필요가 없습니다. 방금 문법 규칙을 수정했습니다. 우리 는 의미 분석을 변경해야했습니다. C ++ 11은 의미 상 매우 복잡하며이 작업은 파서를 실행하기위한 노력을 엄청나게합니다.
2015 년 2 월 편집 : … 이제 전체 C ++ 14를 처리합니다. ( 간단한 코드 비트의 GLR 구문 분석 및 C ++의 악명 높은 “가장 성가신 구문 분석”에 대해서는 C ++ 코드에서 사람이 읽을 수있는 AST 가져 오기를 참조하십시오 .)
2017 년 4 월 편집 : 이제 (초안) C ++ 17을 처리합니다.
답변
Clang의 파서는 다른 오픈 소스 및 상용 C 및 C ++ 프런트 엔드와 마찬가지로 손으로 작성한 재귀 하강 파서입니다.
Clang은 여러 가지 이유로 재귀 하강 파서를 사용합니다.
- 성능 : 손으로 쓴 파서를 사용하면 빠른 파서를 작성하여 필요에 따라 핫 경로를 최적화 할 수 있으며 항상 해당 성능을 제어 할 수 있습니다. 빠른 구문 분석기를 사용하면 “실제”구문 분석기가 일반적으로 사용되지 않는 다른 개발 도구 (예 : IDE의 구문 강조 표시 및 코드 완성)에서 Clang을 사용할 수 있습니다.
- 진단 및 오류 복구 : 손으로 작성한 재귀 하강 파서로 모든 권한을 가지고 있기 때문에 일반적인 문제를 감지하고 훌륭한 진단 및 오류 복구를 제공하는 특수 사례를 쉽게 추가 할 수 있습니다 (예 : http : //clang.llvm 참조) . .org / features.html # expressivediags ) 자동 생성 된 파서를 사용하면 생성기의 기능으로 제한됩니다.
- 단순성 : 재귀 하강 파서는 작성, 이해 및 디버그가 쉽습니다. 파싱 전문가가되거나 파서를 확장 / 개선하기위한 새로운 도구 (오픈 소스 프로젝트에 특히 중요 함)를 배울 필요는 없지만 여전히 훌륭한 결과를 얻을 수 있습니다.
전반적으로 C ++ 컴파일러의 경우 그다지 중요하지 않습니다. C ++의 구문 분석 부분은 사소하지 않지만 여전히 쉬운 부분 중 하나이므로 단순하게 유지하는 것이 좋습니다. 시맨틱 분석 (특히 이름 조회, 초기화, 과부하 해결 및 템플릿 인스턴스화)은 구문 분석보다 훨씬 더 복잡합니다. 증명을 원한다면 Clang의 “Sema”구성 요소 (의미 분석 용)와 “Parse”구성 요소 (파싱 용)의 코드 배포 및 커밋을 확인하십시오.
답변
gcc의 파서는 손으로 작성되었습니다. . 나는 clang에 대해서도 동일하다고 생각합니다. 이는 아마도 몇 가지 이유 때문일 것입니다.
- 성능 : 특정 작업에 대해 직접 최적화 한 것이 거의 항상 일반적인 솔루션보다 더 나은 성능을 발휘합니다. 추상화에는 일반적으로 성능 저하가 있습니다.
- 타이밍 : 적어도 GCC의 경우 GCC는 많은 무료 개발자 도구보다 앞서 있습니다 (1987 년에 출시됨). 당시에는 무료 버전의 yacc 등이 없었습니다. FSF 사람들의 우선 순위가 될 것이라고 생각합니다.
이것은 아마도 “여기에서 발명되지 않음”증후군의 경우가 아니라 “우리가 필요로하는 것에 특별히 최적화 된 것이 없기 때문에 우리가 직접 작성했습니다”라는 문구에 더 가깝습니다.
답변
이상한 대답!
C / C ++ 문법은 문맥이 자유롭지 않습니다. Foo * 막대 때문에 문맥에 민감합니다. 모호. Foo가 유형인지 아닌지 알기 위해 typedef 목록을 작성해야합니다.
Ira Baxter : 당신의 GLR에 대한 요점이 보이지 않습니다. 모호성을 포함하는 구문 분석 트리를 만드는 이유. 구문 분석은 모호성을 해결하고 구문 트리를 구축하는 것을 의미합니다. 두 번째 단계에서 이러한 모호성을 해결하므로 덜 추하지 않습니다. 나를 위해 그것은 훨씬 더 추합니다 …
Yacc는 LR (1) 파서 생성기 (또는 LALR (1))이지만 상황에 맞게 쉽게 수정할 수 있습니다. 그리고 그 안에 못생긴 것은 없습니다. Yacc / Bison은 C 언어 구문 분석을 돕기 위해 만들어 졌으므로 아마도 C 구문 분석기를 생성하는 가장 못된 도구는 아닐 것입니다.
GCC 3.x까지 C 구문 분석기는 yacc / bison에 의해 생성되며 구문 분석 중에 typedefs 테이블이 작성됩니다. “in parse”typedefs 테이블 빌드를 사용하면 C 문법이 로컬 컨텍스트에서 자유 로워지고 더욱 “로컬 LR (1)”이됩니다.
이제 Gcc 4.x에서는 재귀 하강 파서입니다. Gcc 3.x에서와 똑같은 파서이며 여전히 LR (1)이며 동일한 문법 규칙을 가지고 있습니다. 차이점은 yacc 파서가 수작업으로 다시 작성되었으며 시프트 / 리 듀스가 이제 호출 스택에 숨겨져 있으며 gcc 3.x yacc 에서처럼 “state454 : if (nextsym == ‘(‘) goto state398″이 없다는 것입니다. parser를 사용하면 패치, 오류 처리 및 더 좋은 메시지를 인쇄하고 구문 분석 중에 다음 컴파일 단계를 수행하는 것이 더 쉽습니다. gcc noob에 대한 “읽기 쉬운”코드가 훨씬 적습니다.
왜 그들은 yacc에서 재귀 하강으로 전환 했습니까? C ++를 구문 분석하기 위해 yacc를 피해야하고 GCC가 다국어 컴파일러를 꿈꾸기 때문에 컴파일 할 수있는 다른 언어간에 최대 코드를 공유하기 때문입니다. 이것이 C ++와 C 파서가 같은 방식으로 작성된 이유입니다.
C ++는 C와 같이 “로컬”LR (1)이 아니고 LR (k)도 아니기 때문에 C보다 파싱하기가 더 어렵습니다. 봐가에 func<4 > 2>
있는 즉, 4> 2 인스턴스화 템플릿 함수 func<4 > 2>
로 읽을 수있다 func<1>
. 이것은 확실히 LR (1)이 아닙니다. 이제 func<4 > 2 > 1 > 3 > 3 > 8 > 9 > 8 > 7 > 8>
. 이것은 재귀 하강이 몇 가지 추가 함수 호출의 대가로 모호성을 쉽게 해결할 수있는 곳입니다 (parse_template_parameter는 모호한 파서 함수입니다. parse_template_parameter (17tokens)가 실패하면 parse_template_parameter (15tokens), parse_template_parameter (13tokens) …까지 다시 시도하십시오. 효과가있다).
yacc / bison 재귀 하위 문법에 추가 할 수없는 이유를 모르겠습니다. 아마도 이것이 gcc / GNU 파서 개발의 다음 단계가 될까요?
답변
GCC와 LLVM-Clang은 기계 생성 Bison-Flex 기반 상향식 파싱이 아닌 손으로 쓴 재귀 하강 파서를 사용하는 것 같습니다.
특히 들소는 모호하게 파싱하고 나중에 두 번째 패스를 수행하지 않고는 문법을 다룰 수 없다고 생각합니다.
Haskell의 Happy가 C 구문의 특정 문제를 해결할 수있는 모나 딕 (즉, 상태 종속) 파서를 허용한다는 것을 알고 있지만 사용자가 제공하는 상태 모나드를 허용하는 C 파서 생성기가 없다는 것을 알고 있습니다.
이론적으로 오류 복구는 손으로 쓴 파서를 선호하지만 GCC / Clang에 대한 나의 경험은 오류 메시지가 특히 좋지 않다는 것입니다.
성능에 관해서는 일부 주장이 입증되지 않은 것 같습니다. 파서 생성기를 사용하여 큰 상태 머신을 생성하면 무언가가 발생해야하며 O(n)
파싱이 많은 도구에서 병목 현상이 아닌지 의심합니다.
답변
