파서 결합기와 함께 별도의 파싱 및 렉싱 패스가 모범 사례입니까?


18

파서 결합기를 사용하기 시작했을 때 나의 첫 번째 반응은 파싱과 렉싱 사이의 인공적인 구별과 같은 느낌에서 해방 된 느낌이었습니다. 갑자기 모든 것이 파싱되었습니다!

그러나 최근 에이 구별을 복원하는 사람을 보여주는 codereview.stackexchange 에서이 게시물을 발견했습니다. 처음에는 이것이 매우 어리석은 일이라고 생각했지만 Parsec 에이 동작을 지원하기 위해 함수가 존재한다는 사실은 나 자신에게 의문을 제기합니다.

파서 콤비 네이터에서 이미 렉싱 된 스트림을 구문 분석 할 때의 장점 / 단점은 무엇입니까?


누군가가 [parser-combinator] 태그를 추가 할 수 있습니까?
Eli Frey

답변:


15

구문 분석에서 문맥없는 언어의 분석을 가장 자주 이해합니다. 컨텍스트 프리 언어는 일반 언어보다 강력하므로 파서는 어휘 분석기의 역할을 즉시 수행 할 수 있습니다.

그러나 이것은 a) 매우 부자연 스럽습니다. b) 종종 비효율적입니다.

내가 생각 경우)의 경우, 어떻게 예를 들어 if경우 표현의 외모, 생각 expr이 THEN EXPR ELSE EXPR 이 아니라 '내가' 'F', 어쩌면 약간의 공간, 그 다음 모든 문자가 식을 시작할 수 등 당신이 얻을 생각.

b) 식별자, 리터럴, 모든 종류의 대괄호와 같은 어휘 실체를 인식하는 훌륭한 작업을 수행하는 강력한 도구가 있습니다. 실제로 도구는 실제로 시간 내에 작업을 수행하고 멋진 인터페이스 인 토큰 목록을 제공합니다. 더 이상 파서에서 공백을 건너 뛸 염려가 없습니다. 파서는 문자가 아닌 토큰을 처리 할 때 훨씬 더 추상적입니다.

결국 파서가 저수준의 물건으로 바쁘다고 생각한다면 왜 문자를 처리합니까? 비트 수준에서도 쓸 수 있습니다! 비트 레벨에서 작동하는 파서는 거의 이해할 수 없을 것입니다. 문자 및 토큰과 동일합니다.

그냥 내 2 센트.


3
정확성을 위해 파서는 항상 어휘 분석기의 역할을 수행 할 수 있습니다 .
Giorgio

또한 효율성과 관련하여 파서의 효율성이 떨어지는 지 확실하지 않습니다. 결과 문법에는 정규 언어를 설명하는 하위 문법이 포함될 것으로 예상되며 해당 하위 문법의 코드는 해당 어휘 분석기만큼 빠릅니다. IMO의 진정한 요점은 (a) : 더 단순하고 더 추상적 인 파서로 작업하는 것이 얼마나 자연스럽고 직관적인지입니다.
Giorgio

@Giorgio-첫 번째 의견에 관하여 : 당신 말이 맞아요. 내가 여기서 명심 한 것은 어휘 분석기가 문법을 더 쉽게 만드는 작업을 실용적으로 수행하여 LALR (2) 대신 LALR (1)을 사용할 수있는 경우이다.
Ingo

2
추가 실험과 성찰을 통해 귀하의 답변에 대한 동의를 제거했습니다. 둘 다 Antlr 등의 세계에서 온 것 같습니다. 파서 결합기의 첫 번째 클래스 특성을 고려할 때 필자는 종종 토큰 파서에 대한 래퍼 파서를 정의하여 파서의 파싱 계층에서 각 토큰을 단일 이름으로 남겨 둡니다. 예를 들어 if 예제는 다음과 같습니다 if = string "if" >> expr >> string "then" >> expr >> string "else" >> expr.
Eli Frey

1
성능은 여전히 ​​공개 질문입니다. 벤치 마크를하겠습니다.
Eli Frey

8

렉싱과 파싱을 분리하는 것이 "좋은 습관"이라고 제안하는 모든 사람들은 동의하지 않습니다. 단일 패스로 렉싱과 파싱을 수행하면 훨씬 더 많은 힘을 얻을 수 있으며 성능에 미치는 영향은 제시된 것만 큼 나쁘지 않습니다. 다른 답변 ( Packrat 참조 ).

이 접근 방식은 단일 입력 스트림에서 여러 언어를 혼합해야 할 때 빛납니다. 이 단지 같은 이상한 메타 프로그래밍 지향 언어가 필요하지 않습니다 카타딘비슷 하지만, 글을 읽고 프로그래밍 같은뿐만 아니라 훨씬 더 메인 스트림 애플리케이션에 HTML에 자바 스크립트를 채우고, 주석에서 HTML을 사용하여, (C ++, 말, 라텍스를 혼합), 및 곧.


내 대답에서 나는 그것이 "모든 상황에서 더 나은 연습"이 아니라 "특정 상황에서 좋은 연습"이라고 제안했다.
Giorgio

5

어휘 분석기는 일반 언어를 인식하고 구문 분석기는 컨텍스트없는 언어를 인식합니다. 각 정규 언어에는 컨텍스트가 없으므로 (소위 오른쪽 선형 문법 으로 정의 할 수 있음 ) 파서는 일반 언어를 인식 할 수 있으며 파서와 어휘 분석기의 구별은 불필요한 복잡성을 추가하는 것으로 보입니다. 단일 컨텍스트 프리 문법 (구문 분석기)은 파서와 어휘 분석기의 역할을 수행 할 수 있습니다.

반면에 일반 언어 (및 어휘 분석기)를 통해 컨텍스트가없는 언어의 일부 요소를 캡처하는 것이 유용 할 수 있습니다.

  1. 이러한 요소는 종종 숫자 및 문자열 리터럴, 키워드, 식별자 인식, 공백 건너 뛰기 등의 표준 방식으로 처리 할 수 ​​있도록 자주 나타납니다.
  2. 토큰의 정규 언어를 정의하면 결과가없는 문맥없는 문법이 더 단순 해집니다. 예를 들어 개별 문자가 아닌 식별자의 관점에서 추론하거나 특정 언어와 관련이없는 공백을 완전히 무시할 수 있습니다.

따라서 어휘 분석에서 구문 분석을 분리하면 더 간단한 컨텍스트 프리 문법으로 작업하고 어휘 분석기에서 몇 가지 기본 (일반적으로 일상적인) 태스크를 캡슐화 할 수 있다는 장점이 있습니다 (divide et impera).

편집하다

파서 결합기에 익숙하지 않으므로 위의 고려 사항이 해당 컨텍스트에 어떻게 적용되는지 잘 모르겠습니다. 필자는 파서 조합기를 사용하더라도 컨텍스트가없는 문법이 하나만 있어도 두 수준 (어휘 분석 / 파싱)을 구분하면이 문법을 더 모듈화 할 수 있습니다. 전술 한 바와 같이, 낮은 어휘 분석 계층은 식별자, 리터럴 등에 대한 재사용 가능한 기본 파서를 포함 할 수있다.


2
모든 어휘 분석기는 정규식 엔진을 기반으로하기 때문에 Lexemes는 자연스럽게가 아니라 일반적인 문법에 속합니다. 디자인 할 수있는 언어의 표현력을 제한하고 있습니다.
SK-logic

1
일반 언어로 설명 할 수없는 욕구를 정의하는 것이 적절한 언어의 예를 들어 줄 수 있습니까?
Giorgio

1
예를 들어, 내가 만든 도메인 특정 언어에서 식별자는 TeX 표현식 일 수 있습니다.이 코드는 예를 들어, \alpha'_1 (K_0, \vec{T})\ alpha'_1, K_0 및 \ vec {T} 식별자입니다.
SK-logic

1
문맥이없는 문법이 있다면 항상 비 터미널 N을 가져 와서 그 자체로 유용한 의미를 갖는 단위 (예 : 표현, 용어, 숫자, 문장)로 파생 될 수있는 단어를 취급 할 수 있습니다. 이것은 해당 장치 (파서, 파서 + 렉서 등)를 구문 분석하는 방법에 관계없이 수행 할 수 있습니다. IMO 파서 + 어휘 분석기의 선택은 의미론적인 것 (구문 분석하는 소스 코드 블록의 의미)보다 기술적 인 것 (구문을 구현하는 방법)입니다. 어쩌면 나는 무언가를 내려다보고 있지만 두 가지 측면은 나에게 직교 해 보입니다.
Giorgio

3
따라서 나는 당신에게 동의합니다 : 임의의 기본 빌딩 블록 ( lexemes ) 을 정의 하고 어휘 분석기를 사용하여 인식하려는 경우 이것이 항상 가능한 것은 아닙니다. 이것이 이것이 렉서의 목표인지 궁금합니다. 내가 이해하는 한 어휘 분석기의 목표는 기술적 인 것입니다. 파서에서 저수준의 지루한 구현 세부 정보를 제거합니다.
Giorgio

3

간단히 말해서, 렉싱과 파싱은 서로 다른 복잡성 때문에 분리되어야합니다. Lexing은 DFA (deterministic finite automaton)이고 파서는 PDA (push-down automaton)입니다. 이는 파싱이 본질적으로 렉싱보다 더 많은 리소스를 소비한다는 것을 의미하며 DFA에만 사용할 수있는 특정 최적화 기술이 있습니다. 또한 유한 상태 머신을 작성하는 것은 훨씬 덜 복잡하며 자동화하기가 더 쉽습니다.

구문 분석 알고리즘을 사용하여 lex에 낭비하고 있습니다.


구문 분석기를 사용하여 어휘 분석을 수행하는 경우 PDA는 스택을 사용하지 않으며 기본적으로 DFA로 작동합니다. 입력을 소비하고 상태 간을 점프하는 것입니다. 100 % 확신 할 수는 없지만 DFA에 적용 할 수있는 최적화 기술 (상태 수 감소)을 PDA에도 적용 할 수 있다고 생각합니다. 그러나 그렇습니다. 더 강력한 도구를 사용하지 않고 어휘 분석기를 작성하고 그 위에 더 간단한 구문 분석기를 작성하는 것이 더 쉽습니다.
Giorgio

또한 모든 것을보다 유연하고 유지 보수 할 수있게합니다. 예를 들어, 레이아웃 규칙이없는 (즉 세미콜론과 중괄호가있는) Haskell 언어에 대한 파서가 있다고 가정합니다. 우리가 별도의 어휘 분석기를 가지고 있다면, 토큰에 대해 다른 패스를 수행하고 필요에 따라 중괄호와 세미콜론을 추가하여 레이아웃 규칙을 추가 할 수 있습니다. 또는 더 쉬운 예를 들어, 식별자로만 ASCII 문자를 지원하는 언어로 시작하여 이제 식별자로 유니 코드 문자를 지원하려고한다고 가정합니다.
Ingo

1
@ Ingo, 왜 별도의 어휘 분석기에서해야합니까? 해당 터미널을 제외하십시오.
SK-logic

1
@ SK-logic : 귀하의 질문을 이해하지 못했습니다. 별도의 어휘 분석기가 좋은 선택이 될 수있는 이유는 무엇입니까?
Ingo

조르지오 스택은 일반적인 LALR 스타일 파서의 중요한 구성 요소입니다. 파서로 렉싱을 수행하면 메모리가 많이 낭비되고 (정적 저장소와 동적으로 할당 됨) 속도가 훨씬 느려집니다. Lexer / Parser 모델은 효율적입니다. 사용 :)
riwalk

1

별도의 구문 분석 / lex의 주요 장점 중 하나는 토큰 스트림이라는 중간 표현입니다. 이것은 lex / parse가 결합되어 불가능한 다양한 방식으로 처리 될 수 있습니다.

즉, 나는 좋은 재귀 괜찮은 것이 파서 생성기를 배우는 것보다 덜 복잡하고 다루기가 더 쉬울 수 있으며 파서 생성기의 규칙 내에서 그래머의 약점을 표현하는 방법을 찾아야한다는 것을 알았습니다.


조립식 스트림에서보다 쉽게 ​​표현 된 다음 구문 분석시 수행되는 문법에 대해 더 자세히 설명해 주시겠습니까? 나는 장난감 언어와 몇 가지 데이터 형식을 구현 한 경험이 있으므로 무언가를 놓친 것 같습니다. 수동 롤링 RD 파서 / 렉스 콤보와 BNF 공급 (가정) 발전기 사이의 성능 특성에 주목 했습니까?
Eli Frey
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.