파서에 문법을 어떻게 지정해야합니까?


12

나는 몇 년 동안 프로그래밍을 해왔지만 여전히 시간이 오래 걸리는 한 가지 작업은 파서에 문법을 지정하는 것입니다.이 과도한 노력 후에도 내가 생각해 낸 문법이 좋은지 확신 할 수 없습니다 ( 합리적인 "좋은"수단으로).

문법을 지정하는 프로세스를 자동화하는 알고리즘이 있다고 기대하지는 않지만 현재 접근 방식의 많은 추측과 시행 착오를 제거하는 문제를 구조화하는 방법이 있기를 바랍니다.

나의 첫 생각은 파서에 대해 읽어야했고, 나는 이것의 일부를 해봤지만,이 주제에 관해 읽은 모든 것은 문법을 주어진 것 (또는 검사로 지정할 수있을 정도로 사소한)을 취하고, 이 문법을 파서로 번역하는 문제. 문법 문제를 먼저 지정하는 방법에 대한 직전의 문제에 관심이 있습니다.

나는 주로 구체적인 예제 모음 (양수와 음수)을 공식적으로 나타내는 문법 을 지정하는 문제에 관심이 있습니다. 이것은 새로운 구문 을 디자인하는 문제와 다릅니다 . 이 차이점을 지적한 Macneil에게 감사드립니다.

나는 문법과 구문의 차이점에 대해 정말로 감사 한 적이 없었지만 이제는 문법을 이해하기 시작하면서 문법을 구체화하는 문법을 지정하는 문제에 주로 관심이 있다고 말함으로써 첫 번째 설명을 선명하게 할 수있었습니다. 사전 정의 된 구문 : 제 경우에는이 구문의 기초가 일반적으로 긍정적이고 부정적인 예의 모음입니다.

구문 분석기에 문법이 어떻게 지정됩니까? 모범 사례, 설계 방법론 및 구문 분석기의 문법 지정에 대한 기타 유용한 정보를 설명하기위한 사실상의 표준 인 책이나 참조가 있습니까? 파서 문법을 읽을 때 어떤 점에 중점을 두어야합니까?


1
실제 문제에 중점을두기 위해 질문을 약간 편집했습니다. 이 사이트는 문법과 파서에 대한 질문을하고 전문가의 답변을 얻을 수있는 곳입니다. 살펴볼 가치가있는 외부 리소스가 있다면 직접 도움이되는 답변에서 자연스럽게 나타날 것입니다.
Adam Lear

8
@kjo 불행한 일입니다. 당신이 요구하는 모든 것이 참조 목록이라면, 스택 교환을 최대한 활용하지 않는 것입니다. 메타 문제가 의도 한대로 사이트를 사용하고 있지 않습니다. 질문과 대답은 Q & A 모델에 잘 맞지 않기 때문에 Stack Exchange에서 거의 권장 하지 않습니다. 항목, 아이디어 또는 의견이 아닌 답변이있는 질문을하는 쪽으로 사고 방식을 전환하는 것이 좋습니다 .
Adam Lear

3
@kjo 확실히 질문이지만 Stack Exchange 에 물어 보는 올바른 질문은 아닙니다 . SE는 참조 목록을 작성하지 않습니다. 그것은 여기의 참조. 자세한 설명은 내 의견에 링크 된 메타 게시물을 읽으십시오 .
Adam Lear

5
@kjo : 낙심하지 마십시오! Anna의 편집 내용은 질문의 핵심과 핵심을 유지했으며, Programmers.SE에서 기대하는 형식보다 질문을 더 많이 만들어서 도움을주었습니다. 나는 당신이 찾는 확실한 언급이 없다는 것을 알고 있지만 답을 줄 수는있었습니다. [OTOH, 나는 그런 참고 문헌을 알고 있었다면 분명히 그것을 포함했을 것입니다.] 우리는 내 경우와 같은 더 많은 답변을 장려하고 싶습니다. 다른 사람들과 이야기하는 경험.
Macneil

4
@kjo 나는 Anna의 편집으로 롤백 했으며 책 권장 사항대한 지침을 기반으로 표준 참조에 대한 특정 호출을 통합하려고 시도했습니다 . 제공된 답변에 많은 좋은 정보가 있으며, 책을 찾는 것에 관한 질문은 낭비 일 것입니다. 이제 우리가 편집 전쟁으로 멈출 수 있다면 정말 좋을 것입니다.

답변:


12

샘플 파일에서 해당 예제에서 일반화하려는 정도에 따라 결정을 내려야합니다. 다음 세 가지 샘플이 있다고 가정하십시오 (각각 별도의 파일 임).

f() {}
f(a,b) {b+a}
int x = 5;

다음 샘플을 받아 들일 문법을 간단하게 지정할 수 있습니다.

사소한 문법 하나 :

start ::= f() {} | f(a,b) {b+a} | int x = 5;

사소한 문법 2 :

start ::= tokens
tokens ::= token tokens | <empty>
token ::= identifier | literal | { | } | ( | ) | , | + | = | ;

첫 번째 샘플은 세 개의 샘플 허용하므로 사소 합니다. 두 번째 토큰은 토큰 유형을 사용할 수있는 모든 것을 허용하므로 사소한 입니다. [이 토론에서는 토큰 화기 디자인에 대해 크게 신경 쓰지 않는다고 가정합니다. 토큰으로 식별자, 숫자 및 문장 부호를 가정하는 것이 간단하며 원하는 스크립팅 언어에서 토큰 세트를 빌릴 수 있습니다 어쨌든.]

따라서 따라야 할 절차는 높은 수준에서 시작하여 "각 인스턴스 중 몇 개를 허용 하시겠습니까?"를 결정하는 것입니다. 구문 구조가 method클래스의 s 와 같이 여러 번 반복되는 것이 합리적이라면 다음 형식의 규칙이 필요합니다.

methods ::= method methods | empty

EBNF 에서 다음과 같이 더 잘 언급 됩니다.

methods ::= {method}

인스턴스가 0 개 또는 1 개만 필요할 때 ( extendsJava 클래스 의 절에서 와 같이 구성이 선택 사항임을 의미 함 ) 또는 선언에서 변수 이니셜 라이저를 사용하여 하나 이상의 인스턴스를 허용하려는 경우에 분명합니다. ). 요소 사이에 구분 기호가 필요하거나 ( ,인수 목록에서 와 같이 ), 각 요소 뒤에 종결자가 ;필요하거나 (구문을 분리 할 때와 같이 ), 구분 기호 나 종결자가 필요하지 않은 경우 와 같은 문제를 염두에 두어야합니다 . 클래스의 메소드 사용).

언어에서 산술 연산 식을 사용하는 경우 기존 언어의 우선 순위 규칙에서 쉽게 복사 할 수 있습니다. 이국적인 표현을 찾는 것보다 C의 표현 규칙과 같이 잘 알려진 것을 고수하는 것이 가장 좋습니다.

우선 순위 문제 (서로 구문 분석되는 것) 및 반복 문제 (각 요소의 수, 발생 방법은 어떻게됩니까?) 외에도 순서에 대해서도 고려해야합니다. 항상 다른 것 앞에 무언가가 나타나야합니까? 한 가지가 포함 된 경우 다른 것을 제외해야합니까?

이 시점에서 일부 규칙을 문법적으로 시행하고 싶을 수도 있습니다. 예를 들어 Person연령을 지정하는 경우 생년월일을 지정하지 않으려는 규칙과 같은 규칙 도 있습니다. 문법을 구성 할 수는 있지만 모든 것을 파싱 한 후 "의미 적 확인"패스를 사용하여이를 쉽게 적용 할 수 있습니다. 이것은 문법을 더 단순하게 유지하며 규칙을 위반했을 때 더 나은 오류 메시지를 만듭니다.


1
더 나은 오류 메시지를 보려면 +1 귀하의 언어를 사용하는 대부분의 사용자는 천만 또는 천만에 관계없이 전문가가 아닙니다. 파싱 ​​이론은이 측면을 너무 오랫동안 무시했다.
MSalters

10

파서의 문법을 지정하는 방법을 어디서 배울 수 있습니까?

대부분의 파서 생성기의 경우 일반적으로 Backus-Naur<nonterminal> ::= expression 형식 의 변형입니다 . 나는 당신이 그런 것을 사용하고 파서를 직접 만들려고하지 않는다는 가정을 계속할 것입니다. 구문이 주어진 형식에 대한 파서를 생성 할 수 있다면 (아래 샘플 문제를 포함 시켰음), 문법을 지정하는 것은 문제가되지 않습니다.

내가 생각하는 것은 샘플 세트에서 구문을 구분하는 것입니다.이 구문 분석보다 패턴 인식이 실제로 더 많습니다. 그것에 의존해야한다면 데이터를 제공하는 사람은 형식을 잘 처리하지 못하기 때문에 구문을 제공 할 수 없습니다. 뒤로 밀고 그들에게 공식적인 정의를하도록 지시하는 옵션이 있다면 그렇게하십시오. 나쁜 입력을 받아들이거나 좋은 입력을 거부하는 추론 구문에 따라 파서의 결과에 대해 책임을 질 수 있다면 모호한 문제를 일으키는 것은 불공평합니다.

... 내가 찾은 문법이 어느 정도 좋은지 확신 할 수 없다 ( "합리적"의 합리적인 척도).

귀하의 상황에서 "좋음"은 "긍정을 분석하고 부정을 거부합니다"를 의미해야합니다. 입력 파일의 구문에 대한 다른 공식 사양이 없으면 샘플은 유일한 테스트 사례이며 그보다 더 나은 방법은 없습니다. 발을 내려 놓고 긍정적 인 예만 좋다고 말하고 다른 것을 배제 할 수는 있지만 아마도 성취하려는 목표에 맞지 않을 수도 있습니다.

정상적인 환경에서 문법 테스트는 다른 테스트와 비슷합니다. 비 터미널 (및 단말기가 어휘 분석기에 의해 생성되는 경우)의 모든 변형을 수행 할 수있는 충분한 테스트 사례를 마련해야합니다.


샘플 문제

아래 규칙에 정의 된대로 목록이 포함 된 텍스트 파일을 구문 분석하는 문법을 작성하십시오 .

  • 목록은 0 개 이상의 구성 .
  • 것은 구성 식별자 , 오픈 중괄호, 항목 목록 및 닫는 중괄호.
  • _item_list_는 0 개 이상의 항목으로 구성됩니다 .
  • 식별자 , 등호, 다른 식별자 및 세미콜론 의 항목 구성
  • 식별자 문자 AZ, AZ, 0-9 또는 밑줄 하나 이상의 시퀀스이다.
  • 공백은 무시됩니다.

입력 예 (모두 유효) :

clank { foo = bar; baz = bear; }
clunk {
    quux =bletch;
    281_apple = OU812;
    He_Eats=Asparagus ; }

2
또한 BNF 자체가 아닌 "Backus-Naur의 일부 변형"을 사용해야합니다. BNF는 문법을 표현할 수 있지만 목록과 같은 매우 일반적인 개념을 필요한 것보다 훨씬 복잡하게 만듭니다. 이러한 문제를 개선하는 EBNF와 같은 다양한 개선 된 버전이 있습니다.
메이슨 휠러

7

Macneil과 Blrfl의 답변은 훌륭합니다. 프로세스의 시작 부분에 대한 의견을 추가하고 싶습니다.

구문은 표현하는 단지 방법입니다 프로그램을 . 언어에 대한 문법은이 질문에 대한 답으로 결정해야합니다 : 프로그램이란 무엇입니까?

프로그램이 클래스 모음이라고 말할 수 있습니다. 알았어

program ::= class*

출발점으로. 아니면 써야 할 수도 있습니다

program ::= ε
         |  class program

자, 수업이란 무엇입니까? 이름이 있습니다. 선택적 수퍼 클래스 사양; 그리고 많은 생성자, 메소드 및 필드 선언. 또한 클래스를 단일 (모호하지 않은) 단위로 그룹화하는 방법이 필요하며 이는 유용성에 대한 양보를 포함해야합니다 (예 : 예약어로 태그 ​​지정 class). 괜찮아:

class ::= "class" identifier extends-clause? "{" class-member-decl * "}"

그것은 당신이 선택할 수있는 하나의 표기법 ( "콘크리트 구문")입니다. 또는 다음과 같이 쉽게 결정할 수 있습니다.

class ::= "(" "class" identifier extends-clause "(" class-member-decl* ")" ")"

또는

class ::= "class" identifier "=" "CLASS" extends-clause? class-member-decl* "END"

이미 예제를 가지고 있다면 이미이 결정을 내재적으로 내렸을 것입니다. 그러나 요점을 강조하고 싶습니다 . 구문의 구조는 그것이 나타내는 프로그램의 구조에 의해 결정됩니다. 그것이 Macneil의 답변에서 "사소한 문법"을 넘어서는 것입니다. 그러나 예제 프로그램은 여전히 ​​매우 중요합니다. 그들은 두 가지 목적으로 사용됩니다. 먼저, 추상적 인 수준에서 프로그램이 무엇인지 파악하도록 도와줍니다. 둘째, 언어 구조를 표현하기 위해 어떤 구체적인 구문을 사용해야하는지 결정하는 데 도움이됩니다.

일단 구조가 다운되면 돌아가서 공백 및 주석 허용, 모호성 수정 등과 같은 문제를 처리해야합니다. 이는 중요하지만 전체 디자인에 부차적이며 사용중인 구문 분석 기술.

마지막으로, 당신의 언어에 관한 모든 것을 문법으로 표현하려고하지 마십시오. 예를 들어, 도달 할 수없는 특정 종류의 코드 (예 : returnJava에서와 같이 a 다음에 나오는 명령문)를 금지 할 수 있습니다 . 아마도 문법을 작성하려고 시도해서는 안됩니다. 왜냐하면 사물 (누가 return, 중괄호에있는 경우 또는 if문장 의 두 가지 분기가 모두 돌아 오는 경우 )을 놓치 거나 문법을 너무 복잡하게 만들 수 있기 때문입니다. 관리합니다. 그것은 A의 상황에 맞는 제약; 별도의 패스로 작성하십시오. 상황에 맞는 제약 조건의 또 다른 매우 일반적인 예는 형식 시스템입니다. 1 + "a"충분히 열심히 노력했다면 문법 과 같이 식을 거부 할 수는 있지만 거부 할 수는 없습니다 1 + x( x문자열 유형이있는 경우). 그래서문법에 반 구운 제한피하고 별도의 패스로 올바르게 수행하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.