파서 조합기를 언제 사용해야합니까? 파서 생성기를 언제 사용합니까?


59

나는 최근에 나만의 프로그래밍 언어를 만들고 싶어 파서 세계에 대해 심도있게 뛰어 들었다.

그러나 파서 작성에는 두 가지 방법이 있습니다. 파서 생성기 및 파서 조합기.

흥미롭게도, 어떤 경우에 어떤 접근 방식이 더 나은지 설명하는 자료를 찾을 수 없었습니다. 오히려, 많은 자원 (과 사람들은) 나는 주제 만 설명하는 다른 방법을 알고하지 않았다에 대한 조회 와 같은 접근 방식 접근 방식과 다른 전혀 언급하지 :

  • 유명한 드래곤 책은 렉싱 / 검사로 전환 및 (f) 렉스를 언급하지만, 전혀 파서 콤비를 언급하지 않습니다.
  • 언어 구현 패턴 은 Java에 내장 된 ANTLR 파서 생성기에 크게 의존하며 파서 조합자를 언급하지 않습니다.
  • Haskell의 Parser Combinator 인 Parsec의 Parsec 소개 자습서에는 Parser Generator가 언급되어 있지 않습니다.
  • 가장 잘 알려진 C ++ 파서 조합기 인 Boost :: spirit 에는 파서 생성기가 전혀 언급되어 있지 않습니다.
  • 파서 콤비 네이터를 발명 한 위대한 설명 블로그 게시물 파서 생성기를 전혀 언급하지 않습니다.

간단한 개요 :

파서 생성기

파서 생성기는 Extended Backus-Naur 형식의 일부인 DSL로 작성된 파일을 가져 와서 소스 코드로 변환하여이 DSL에 설명 된 입력 언어에 대한 파서가 될 수 있습니다 (컴파일시).

이는 컴파일 프로세스가 두 개의 별도 단계로 수행됨을 의미합니다. 흥미롭게도 파서 생성기 자체는 컴파일러이기도합니다 (많은 것은 실제로 자체 호스팅입니다 ).

파서 조합기

파서 조합기 (Parser Combinator)는 파서 (parsers) 라고하는 간단한 기능을 설명합니다 . 그들은 튜플 반환 (result, rest_of_input), result빈 (예를 들면 수 있습니다 nil또는 Nothing파서는이 입력에서 아무것도를 구문 분석 할 수 없습니다 경우)를. digit파서 가 그 예입니다 . 다른 파서는 당연히 파서를 첫 번째 인자 (최종 인자는 여전히 입력 문자열에 남아 있음)로 받아 들여이를 조합many1수 있습니다.

물론,를 조합 (구성) digit하고 many1새로운 파서를 만들 수 있습니다 integer.

또한 choice파서 목록을 가져 와서 각 파서를 차례로 시도 하는 상위 수준 파서를 작성할 수 있습니다.

이러한 방식으로 매우 복잡한 렉서 / 파서를 구축 할 수 있습니다. 연산자 오버로딩을 지원하는 언어에서는 여전히 대상 언어로 직접 작성되지만 원하는 대상 언어의 모든 기능을 사용할 수 있지만 EBNF와 매우 유사합니다.

간단한 차이점

언어:

  • 파서 생성기는 EBNF-ish DSL과 이러한 명령문이 일치 할 때 생성해야하는 코드의 조합으로 작성됩니다.
  • 파서 조합자는 대상 언어로 직접 작성됩니다.

렉싱 / 파싱 :

  • 파서 생성기는 '렉서'(문자열을 토큰으로 분할하여 어떤 종류의 값을 처리하는지 표시 함)와 '파서'(렉서의 토큰 출력 목록을 가져옴) 사이에 매우 다른 차이점이 있습니다. 그리고 그것들을 결합하여 추상 구문 트리를 형성하려고 시도합니다).
  • 파서 조합자는이 구분이 필요하지 않습니다. 일반적으로 간단한 파서는 '렉서'의 작업을 수행하고 고급 파서는 더 간단한 파서를 호출하여 어떤 종류의 AST 노드를 생성할지 결정합니다.

질문

그러나 이러한 차이점을 감안할 때 (그리고이 차이점 목록은 아마도 완전하지 않습니다!), 나는 어느 것을 사용할 것인지에 대해 교육받은 선택을 수 없습니다 . 이러한 차이점의 의미 / 결과가 무엇인지 알 수 없습니다.

파서 생성기를 사용하면 문제를 더 잘 해결할 수 있다는 문제 속성은 무엇입니까? Parser Combinator를 사용하면 문제를 더 잘 해결할 수있는 문제 속성은 무엇입니까?


4
파서 인터프리터 (구문 파서 언어를 C 또는 Java로 컴파일하는 대신 파서 언어가 직접 실행되는 것을 제외하고 파서 생성기와 유사 함)와 간단히 작성하는 파서 구현 방법에는 두 가지가 더 있습니다. 손으로 파서. 파서를 직접 작성하는 것은 많은 현대 생산 준비가 된 강력한 산업 언어 구현 (예 : GCC, Clang javac,, Scala)에 선호되는 구현 형태입니다 . 그것은 당신에게 어떤 최근 몇 년 동안 ... (좋은 오류 메시지를 생성에 도움이 내부 파서 상태를 통해 대부분의 제어, 제공
요 르그 W MITTAG

3
… 언어 구현 자들에게 매우 높은 우선 순위가되었습니다). 또한 많은 기존 파서 생성기 / 인터프리터 / 콤비 네이터는 현대 언어 구현이 충족해야하는 다양한 요구에 대처하도록 실제로 설계되지 않았습니다. 예를 들어, 많은 현대 언어 구현은 배치 컴파일, IDE 백그라운드 컴파일, 구문 강조, 자동 리팩토링, 지능형 코드 완성, 자동 문서 생성, 자동 다이어그램 작성 등에 동일한 코드를 사용합니다. Scala는 런타임 반영 및 매크로 시스템에 컴파일러를 사용합니다. . 많은 기존 파서…
Jörg W Mittag

1
… 프레임 워크는이를 처리 할만큼 유연하지 않습니다. EBNF를 기반으로하지 않는 파서 프레임 워크도 있습니다. 예를 들어 구문 분석 구문 분석을위한 packrat 파서 .
Jörg W Mittag

2
컴파일하려는 언어에 크게 의존한다고 생각합니다. 어떤 유형입니까 (LR, ...)?
qwerty_so

1
위의 가정은 일반적으로 lexer / LR 파서 조합으로 간단히 컴파일되는 BNF를 기반으로합니다. 그러나 언어가 반드시 LR 문법을 기반으로하는 것은 아닙니다. 그래서 당신은 당신이 컴파일 할 계획입니까?
qwerty_so

답변:


59

지난 며칠 동안 이러한 별도의 기술이 존재하는 이유와 그 강점과 약점이 무엇인지 더 잘 이해하기 위해 많은 연구를했습니다.

이미 존재하는 답변 중 일부는 차이점을 암시했지만 완전한 그림을 제공하지 않았으며 다소 의견이있는 것처럼 보였으 므로이 답변이 작성된 이유입니다.

이 설명은 길지만 중요합니다. 나와 함께 견디십시오 (또는 참을성이없는 경우 플로우 차트를 보려면 끝으로 스크롤하십시오).


파서 조합기와 파서 생성기의 차이점을 이해하려면 먼저 존재하는 다양한 파싱 유형의 차이점을 이해해야합니다.

파싱

파싱은 공식 문법에 따라 일련의 기호를 분석하는 프로세스입니다. (Computing Science에서) 구문 분석은 컴퓨터가 언어로 작성된 텍스트를 이해할 수 있도록하는 데 사용되며, 일반적으로 작성된 텍스트를 나타내는 구문 분석 트리 를 작성하고 트리의 각 노드에 서로 다른 쓰기 부분의 의미를 저장합니다. 그런 다음이 구문 분석 트리는 다른 컴파일러 (많은 컴파일러에서 사용)로 번역하고, 작성된 지침을 어떤 방식으로 (SQL, HTML) 직접 해석하여 Linter 와 같은 도구 가 작업을 수행 하는 등의 다양한 목적으로 사용될 수 있습니다. 등입니다. 때때로 구문 분석 트리가 명시 적으로 나타나지 않습니다.트리의 각 유형의 노드에서 수행해야하는 작업이 직접 실행됩니다. 이렇게하면 효율성이 향상되지만 수 중에는 여전히 암시적인 구문 분석 트리가 있습니다.

파싱은 계산 상 어려운 문제입니다. 이 주제에 대해 50 년이 넘는 연구가 있었지만 여전히 배울 것이 많다.

대략적으로 말하면 컴퓨터가 입력을 구문 분석 할 수있게하는 네 가지 일반적인 알고리즘이 있습니다.

  • LL 파싱. 컨텍스트가없는 하향식 구문 분석입니다.
  • LR 파싱. 컨텍스트가없는 상향식 구문 분석입니다.
  • PEG + Packrat 파싱.
  • 얼리 파싱.

이러한 유형의 구문 분석은 매우 일반적인 이론적 설명입니다. 서로 다른 트레이드 오프로 물리적 시스템에서 이러한 각 알고리즘을 구현하는 여러 가지 방법이 있습니다.

LL 및 LR은 컨텍스트없는 문법 만 볼 수 있습니다 (즉, 작성된 토큰 주위의 컨텍스트는 사용 방법을 이해하는 데 중요하지 않습니다).

PEG / Packrat 구문 분석 및 Earley 구문 분석 은 훨씬 덜 사용됩니다. Earley 구문 분석은 문법을 많이 처리 할 수 ​​있다는 점에서 좋지만 (문맥이 필요없는 구문을 포함하여) 효율적이지 않습니다. 책 (4.1.1 항);이 주장이 여전히 정확한지 확실하지 않습니다). 구문 분석 표현식 문법 + 팩랫 구문 분석 은 비교적 효율적이며 LL 및 LR보다 많은 문법을 처리 할 수 ​​있지만 아래에서 빠르게 다룰 수있는 모호성을 숨 깁니다.

LL (왼쪽에서 오른쪽으로, 가장 왼쪽에서 파생)

이것은 아마도 구문 분석에 대한 가장 자연스러운 방법 일 것입니다. 아이디어는 입력 문자열에서 다음 토큰을보고 트리 구조를 생성하기 위해 가능한 여러 재귀 호출 중 하나를 결정하는 것입니다.

이 트리는 'top-down'으로 만들어졌습니다. 즉, 우리는 트리의 루트에서 시작하여 입력 문자열을 통해 여행하는 것과 같은 방식으로 문법 규칙을 여행합니다. 또한 읽고있는 'infix'토큰 스트림에 해당하는 'postfix'를 구성하는 것으로 볼 수도 있습니다.

LL 스타일 구문 분석을 수행하는 파서는 지정된 원래 문법과 매우 유사하게 작성 될 수 있습니다. 따라서 이해하기 쉽고 디버깅 및 향상시킬 수 있습니다. 클래식 파서 조합기는 LL 스타일 파서를 구축하기 위해 함께 사용할 수있는 '레고 조각'에 지나지 않습니다.

LR (왼쪽에서 오른쪽으로, 가장 오른쪽 파생)

LR 구문 분석은 다른 방식으로 진행합니다. 상향식 : 각 단계에서 스택의 최상위 요소가 문법 목록과 비교되어 문법 의 상위 수준 규칙 으로 축소 될 수 있는지 확인합니다 . 그렇지 않으면 입력 스트림의 다음 토큰이 이동 되고 스택 위에 배치됩니다.

프로그램이 결국 문법에서 시작 규칙을 나타내는 스택에 단일 노드로 끝나는 경우 프로그램이 올바른 것입니다.

Lookahead

이 두 시스템 중 하나에서 어떤 선택을 할 것인지 결정하기 전에 입력에서 더 많은 토큰을 들여다 볼 필요가 있습니다. 이것은이다 (0), (1), (k)또는 (*)당신은 다음과 같은 두 가지 일반적인 알고리즘의 이름 뒤에 참조 - 구문 LR(1) 이나 LL(k). k일반적으로 '문법에서 요구하는만큼'을 *나타내는 반면, 일반적으로 '이 파서는 역 추적을 수행합니다'를 나타내며, 이는 더 강력하고 구현하기는 쉽지만 구문 분석을 계속할 수있는 파서보다 메모리 및 시간 사용량이 훨씬 높습니다. 선형 적으로.

LR 스타일 파서는 이미 '예측'을 결정할 때 스택에 많은 토큰을 가지고 있으므로 파견 할 정보가 더 있습니다. 즉, 동일한 문법에 대해 LL 스타일 파서보다 '선견지명'이 덜 필요한 경우가 많습니다.

LL 대 LR : Ambiguety

위의 두 가지 설명을 읽을 때 LL 스타일 구문 분석이 훨씬 더 자연스럽게 보이기 때문에 LR 스타일 구문 분석이 왜 존재하는지 궁금 할 것입니다.

그러나 LL 스타일 구문 분석에는 왼쪽 재귀 문제가 있습니다.

다음과 같은 문법을 작성하는 것은 매우 당연합니다.

expr ::= expr '+' expr | term term ::= integer | float

그러나이 문법을 구문 분석 할 때 LL 스타일 구문 분석기는 무한 재귀 루프에 고정됩니다. expr규칙 의 가장 왼쪽 가능성을 시도 하면 입력을 사용하지 않고이 규칙으로 다시 반복됩니다.

이 문제를 해결하는 방법이 있습니다. 가장 간단한 방법은 이러한 재귀가 더 이상 발생하지 않도록 문법을 다시 작성하는 것입니다.

expr ::= term expr_rest expr_rest ::= '+' expr | ϵ term ::= integer | float (여기서 ϵ 는 '빈 문자열'을 나타냅니다)

이 문법은 이제 재귀 적입니다. 즉시 읽기가 훨씬 어렵습니다.

실제로, 왼쪽 재귀는 다른 많은 단계들 사이에서 간접적으로 발생할 수 있습니다 . 이것은 찾아보기 어려운 문제입니다. 그러나 해결하려고하면 문법을 읽기가 더 어려워집니다.

Dragon Book의 섹션 2.5는 다음과 같이 말합니다.

우리는 갈등이있는 것처럼 보입니다 : 한편으로는 번역을 용이하게하는 문법이 필요하고, 다른 한편으로는 파싱을 용이하게하는 상당히 다른 문법이 필요합니다. 해결책은 쉬운 번역을 위해 문법으로 시작하고 구문 분석을 용이하게하기 위해 신중하게 변환하는 것입니다. 왼쪽 재귀를 제거함으로써 예측 재귀 하강 변환기에 사용하기에 적합한 문법을 ​​얻을 수 있습니다.

LR 스타일 파서는 상향식에서 트리를 작성하므로이 왼쪽 재귀 문제는 없습니다. 그러나 위와 같은 문법을 LR 스타일 파서 (종종 유한 상태 오토 마톤 (Finite-State Automaton ) 로 구현) 로의 정신 번역은 종종
수백 또는 수천 개의 상태가 있기 때문에 수행하기가 매우 어렵고 오류가 발생하기 쉽습니다. 고려해야 할 상태 전이. LR 스타일 파서는 일반적으로 '컴파일러 컴파일러'라고도하는 파서 생성기에 의해 생성 되는 이유 입니다.

모호성을 해결하는 방법

위의 왼쪽 재귀 모호성을 해결하는 두 가지 방법을 보았습니다. 1) 구문을 다시 작성하십시오. 2) LR 파서를 사용하십시오.

그러나 해결하기 어려운 다른 종류의 모호함이 있습니다. 두 개의 서로 다른 규칙이 동시에 똑같이 적용될 수 있다면 어떨까요?

몇 가지 일반적인 예는 다음과 같습니다.

LL 스타일 및 LR 스타일 파서는 모두 이것에 문제가 있습니다. 연산자 우선 순위를 도입하여 산술 연산 식 구문 분석 문제를 해결할 수 있습니다. 비슷한 방식으로, Dangling Else와 같은 다른 문제는 우선 순위 행동 하나를 고르고 해결함으로써 해결할 수 있습니다. (예를 들어 C / C ++에서 매달려있는 댕글 링은 항상 가장 가까운 'if'에 속합니다).

이에 대한 또 다른 '해결책'은 PEG (Parser Expression Grammar)를 사용하는 것입니다. 이는 위에서 사용한 BNF 문법과 유사하지만 모호한 경우 항상 '첫 번째를 선택하십시오'. 물론 이것은 실제로 문제를 '해결'하는 것이 아니라 모호성이 실제로 존재한다는 사실을 숨 깁니다. 최종 사용자는 파서가 어떤 선택을하는지 알지 못하므로 예기치 않은 결과가 발생할 수 있습니다.

일반적으로 문법에 모호성이 없는지 알 수없는 이유를 포함하여이 게시물보다 훨씬 더 자세한 정보가 더 많이 포함되어 있습니다. 이것이 의미하는 멋진 블로그 기사 LL 및 LR이 있습니다. 도구는 어렵다 . 나는 그것을 강력히 추천 할 수있다. 그것은 내가 지금 이야기하고있는 모든 것을 이해하는 데 많은 도움이되었습니다.

50 년간의 연구

그러나 삶은 계속됩니다. 유한 상태 오토 마톤으로 구현 된 '정상적인'LR 스타일 파서는 수천 개의 상태 + 전환이 종종 필요하며 프로그램 크기에 문제가있는 것으로 나타났습니다. 따라서 SLR ( Simple LR ) 및 LALR (Look-ahead LR )과 같은 변형 을 작성하여 다른 기술을 결합하여 오토 마톤을 더 작게 만들어 파서 프로그램의 디스크 및 메모리 공간을 줄였습니다.

또한 위에 나열된 모호성을 해결하는 또 다른 방법은 모호한 경우 두 가지 가능성을 모두 유지하고 구문 분석 하는 일반화 된 기법 을 사용 하는 것입니다. '올바른'하나)뿐만 아니라 두 가지 모두를 반환하는 경우 (모두가 있음을 나타내는 방식으로)

흥미롭게도, 일반화 된 LR 알고리즘 이 기술 후에, 유사한 접근법이 일반화 된 LL 파서 를 구현하는데 사용될 수 있다는 것이 밝혀졌습니다 . 이는 모호한 문법에 대해 유사하게 빠른 ($ O (n ^ 3) $ 시간 복잡성, $ O (n) 단순한 (LA) LR 파서보다 더 많은 부기를 가지고 있지만 완전히 모호하지 않은 문법의 경우 $는 더 높은 상수 인자를 의미하지만 파서는 훨씬 더 자연스러운 재귀 강하 (위에서 아래로) 스타일로 작성 될 수 있습니다 작성하고 디버그합니다.

파서 조합기, 파서 생성기

그래서이 긴 박람회에서 우리는 이제 질문의 핵심에 도달하고 있습니다.

파서 조합기와 파서 생성기의 차이점은 무엇이며, 언제 다른 것보다 사용해야합니까?

그들은 정말 다른 종류의 짐승입니다.

파서 콤비 네이터 는 사람들이 하향식 파서를 작성하고 이들 중 많은 부분이 공통점 이 있음을 깨달았 기 때문에 만들어졌습니다 .

파서 생성기 는 사람들이 LL 스타일 파서 (예 : LR 스타일 파서)에 문제가없는 파서를 작성하려고했기 때문에 만들어졌습니다. 일반적인 예로는 (LA) LR을 구현하는 Yacc / Bison이 있습니다.

흥미롭게도 요즘에는 풍경이 다소 흐릿합니다.

  • GLL 알고리즘 과 함께 작동하는 파서 조합기 를 작성 하여 모든 종류의 하향식 파싱과 마찬가지로 읽기 / 이해할 수있는 고전적인 LL 스타일 파서의 모호한 문제를 해결합니다.

  • LL 스타일 파서를 위해 파서 생성기를 작성할 수도 있습니다. ANTLR은이를 정확히 수행하고 다른 휴리스틱 (Adaptive LL (*))을 사용하여 클래식 LL 스타일 파서의 모호성을 해결합니다.

일반적으로 원래 문법을 '내부'LR 형식으로 변환하기 때문에 LR 파서 생성기를 만들고 문법에서 실행되는 (LA) LR 스타일 파서 생성기의 출력을 디버깅하기가 어렵습니다. 반면에, Yacc에 / 들소와 같은 도구는 최적화의 많은 년을했고, 많은 사람들이 지금으로 고려하는 것이 의미 야생에서 사용을 많이 보았다 구문 분석을 수행하는 방법과 새로운 접근을 향한 회의적입니다.

어떤 것을 사용해야하는지는 문법이 얼마나 어려운지, 파서가 얼마나 빠를 지에 달려 있습니다. 문법에 따라 이러한 기술 중 하나 (다른 기술의 구현)가 더 빠르거나, 메모리 공간이 더 작거나, 디스크 공간이 더 작거나, 다른 것보다 더 확장 가능하거나 디버그하기가 더 쉽습니다. 마일리지가 다를 수 있습니다.

참고 : Lexical Analysis의 주제에 대해

어휘 분석은 파서 조합기와 파서 생성기 모두에 사용될 수 있습니다. 아이디어는 구현하기 매우 쉽고 (따라서 빠른) 소스 코드에 대한 첫 번째 패스를 수행하여 공백, 주석 등을 제거하고 아마도 토큰 화하는 'dumb'파서를 갖는 것입니다. 당신의 언어를 구성하는 다른 요소들

주요 이점은이 첫 번째 단계로 실제 파서를 훨씬 간단하게 만들 수 있다는 것입니다. 가장 큰 단점은 별도의 변환 단계가 있다는 것입니다. 예를 들어 공백을 제거하기 때문에 행 및 열 번호가있는 오류보고가 더 어려워집니다.

결국 어휘 분석기는 또 다른 파서 일 뿐이며 위의 기술 중 하나를 사용하여 구현할 수 있습니다. 단순하기 때문에 종종 주요 파서보다 다른 기술이 사용되며, 예를 들어 여분의 '렉서 생성기'가 존재합니다.


Tl; Dr :

다음은 대부분의 경우에 적용 가능한 플로우 차트입니다. 여기에 이미지 설명을 입력하십시오


@Sjoerd 그것은 매우 어려운 문제로 판명되었으므로 실제로 많은 텍스트입니다. 마지막 단락을 더 명확하게 할 수있는 방법을 알고 있다면, 나는 귀를 기울입니다. "어떻게 사용해야하는지, 문법이 얼마나 어려운지, 구문 분석기가 얼마나 빠를 지에 달려 있습니다. 문법에 따라, 이러한 기술 중 하나 (다른 기술의 구현)는 더 빠르거나, 메모리 풋 프린트가 더 작거나, 디스크 풋 프린트가 더 작거나, 다른 것보다 더 확장 가능하거나 쉽게 디버그 할 수 있습니다. 마일리지가 다를 수 있습니다. "
Qqwy

1
다른 답변은 훨씬 더 짧고 명확하며 답변에 훨씬 더 나은 업무를 수행합니다.
Sjoerd

1
@Sjoerd이 답변을 작성한 이유는 다른 답변이 문제를 지나치게 단순화하여 부분 답변을 완전한 답변으로 제시하거나 일화 오류 로 넘어 가기 때문 입니다. 위의 답변은 Jörg W Mittag, Thomas Killian과의 토론을 구현 한 것입니다. 나는 그들이 사전에 대한 지식을 습득하지 않고 그들이 무엇에 관해 이야기하고 발표했는지 이해 한 후에 질문에 대한 의견을 제시했습니다.
Qqwy

어쨌든 질문에 tl; dr 순서도를 추가했습니다. @Sjoerd를 만족합니까?
Qqwy

2
파서 조합자는 실제로 사용하지 않을 때 문제를 해결하지 못합니다. 단순한 것보다 더 많은 결합기가 있습니다 |. 요점입니다. 올바른 다시 쓰기 expr는 훨씬 간결합니다 expr = term 'sepBy' "+"(여기에서 작은 따옴표는 백틱을 대체하여 문자를 이스케이프 처리하지 않기 때문에 함수를 접두어로 대체합니다). 보다 일반적인 경우에는 chainBy결합기가 있습니다. 필자는 PC에 적합하지 않은 간단한 파싱 작업을 찾기가 어렵지만 실제로 유리한 주장이라고 생각합니다.
Steven Armstrong

8

구문 오류가 없음을 보증하거나 구문 정확성에 대한 전반적인 합격 / 불합격이 괜찮은 입력의 경우, 특히 기능적 프로그래밍 언어에서 구문 분석기 조합기를 사용하는 것이 훨씬 간단합니다. 퍼즐 프로그래밍, 데이터 파일 읽기 등과 같은 상황입니다.

파서 생성기의 복잡성을 추가하려는 기능은 오류 메시지입니다. 사용자에게 행과 열을 가리키는 오류 메시지를 원하며 사람이 이해할 수 있기를 바랍니다. 제대로 수행하려면 많은 코드가 필요하며, antlr과 같은 더 나은 파서 생성기가이를 도와 줄 수 있습니다.

그러나 자동 생성은 지금까지만 가능하며 대부분의 상용 및 장기 오픈 소스 컴파일러는 수동으로 파서를 작성합니다. 나는 당신이 이것을하는 것이 편안하다고 생각한다면, 당신은이 질문을하지 않았을 것이므로 파서 생성기를 사용하는 것이 좋습니다.


2
답변 주셔서 감사합니다! 파서 조합기보다 파서 생성기를 사용하여 읽기 쉬운 오류 메시지를 작성하는 것이 더 쉬운 이유는 무엇입니까? (관계없이 무엇을 우리가 구체적으로 얘기 구현) 예를 들어, 나는 파섹과 성령이 모두 라인 + 열 정보를 포함하는 오류 메시지를 인쇄하는 기능을 포함하는 것을 알고, 그래서뿐만 아니라 파서 콤비에이 일을 확실히 가능 보인다.
Qqwy

파서 결합기로 오류 메시지를 인쇄 할 수있는 것은 아닙니다. 오류 메시지를 믹스에 넣을 때 그 장점이 분명하지 않습니다. 두 가지 방법을 모두 사용하여 비교적 복잡한 문법을 ​​수행하면 무슨 뜻인지 알 수 있습니다.
Karl Bielefeldt

Parser Combinator를 사용하면 정의상 오류 조건에서 얻을 수있는 것은 "이 시점에서 시작하여 올바른 입력을 찾을 수 없습니다"입니다. 이것은 실제로 무엇이 잘못되었는지 알려주지 않습니다. 이론적으로 그 시점에서 호출 된 개별 파서는 예상 한 것과 DIDN이 찾지 못하는 것을 말해 줄 수 있지만 모든 것을 인쇄하면 looooooong 오류 메시지가 나타납니다.
John R. Strohm

1
파서 생성기는 좋은 오류 메시지로도 잘 알려져 있지 않습니다.
Miles Rout

기본적으로 아니요는 아니지만 좋은 오류 메시지를 추가하는 데 더 편리한 후크가 있습니다.
Karl Bielefeldt

4

ANTLR 파서 생성기의 관리자 인 Sam Harwell은 최근 다음같이 썼습니다 .

[콤비 네이터]가 내 요구를 충족하지 않는 것으로 나타났습니다.

  1. ANTLR은 모호함과 같은 것을 관리하기위한 도구를 제공합니다. 개발 중에는 모호한 구문 분석 결과를 보여줄 수있는 도구가 있으므로 문법에서 이러한 모호성을 제거 할 수 있습니다. 런타임에 IDE에서 불완전한 입력으로 인한 모호성을 활용하여 코드 완성과 같은 기능에서보다 정확한 결과를 얻을 수 있습니다.
  2. 실제로 파서 결합기는 내 성능 목표를 달성하기에 적합하지 않다는 것을 알았습니다. 이것의 일부는 되돌아갑니다
  3. 구문 분석 결과가 개요, 코드 완성 및 스마트 들여 쓰기와 같은 기능에 사용되는 경우 문법의 미세한 변경이 해당 결과의 정확성에 영향을주기 쉽습니다. ANTLR은 유형이 다르게 컴파일되는 경우에도 이러한 불일치를 컴파일 오류로 전환 할 수있는 도구를 제공합니다. IDE를 구성하는 모든 추가 코드가 처음부터 새로운 기능에 대한 완전한 경험을 제공한다는 것을 알고 문법에 영향을 미치는 새로운 언어 기능을 확실하게 프로토 타입으로 만들 수 있습니다. ANTLR 4 포크 (C # 대상을 기반으로 함)는이 기능을 제공하려는 시도조차도 알고있는 유일한 도구입니다.

기본적으로 파서 결합기는 놀기 좋은 장난감이지만, 진지한 작업을 위해 잘리지 않습니다.


3

Karl이 언급했듯이 파서 생성기는 더 나은 오류보고 경향이 있습니다. 게다가:

  • 생성 된 코드는 구문에 특화되어 있고 미리보기를위한 점프 테이블을 생성 할 수 있기 때문에 속도가 더 빠릅니다.
  • 모호한 구문을 식별하고, 왼쪽 재귀를 제거하고, 오류 분기를 채우는 등 더 나은 도구를 사용하는 경향이 있습니다.
  • 재귀 정의를 더 잘 처리하는 경향이 있습니다.
  • 발전기가 길어지고 보일러 플레이트를 더 많이 사용하기 때문에 더 견고 해지는 경향이 있습니다.

다른 한편으로, 결합기의 장점은 다음과 같습니다.

  • 코드에 있으므로 런타임에 구문이 다양하면 더 쉽게 변형 할 수 있습니다.
  • 그것들은 연결하기 쉽고 실제로 소비하는 경향이 있습니다 (파서 생성기의 출력은 매우 일반적이며 사용하기 어색합니다).
  • 그것들은 코드 안에 있기 때문에 문법이 당신이 기대하는 것을하지 않을 때 디버깅하기가 조금 더 쉽습니다.
  • 그들은 다른 코드처럼 작동하기 때문에 학습 곡선이 얕습니다. 파서 생성기는 물건을 작동시키는 법을 배우는 자신의 단점이 있습니다.

파서 생성기는 실제 세계에서 사용되는 손으로 쓴 LL 재귀-강하 파서에 비해 끔찍한 오류보고를하는 경향이 있습니다. 파서 생성기는 훌륭한 진단을 추가하는 데 필요한 상태 테이블 전환 후크를 거의 제공하지 않습니다. 이것이 거의 모든 실제 컴파일러가 파서 결합기 또는 파서 생성기를 사용하지 않는 이유입니다. LL 재귀 괜찮은 파서는 "깨끗한"PC / PG는 아니지만 구성하기에는 쉽지 않습니다.
dhchdhd
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.