어휘 분석기를위한 토큰 제공


14

내가 만든 마크 업 언어에 대한 파서를 작성하고 있습니다 (파이썬으로 작성하지만 실제로는이 질문과 관련이 없습니다. 사실 이것이 나쁜 생각처럼 보인다면 더 나은 길에 대한 제안을 좋아합니다) .

파서에 대해 읽고 있습니다 : http://www.ferg.org/parsing/index.html , 나는 lexer를 작성하고 있는데, 올바르게 이해하면 내용을 토큰으로 분할해야합니다. 내가 이해하는 데 어려움이있는 것은 사용해야하는 토큰 유형 또는 생성 방법입니다. 예를 들어, 내가 연결 한 예제의 토큰 유형은 다음과 같습니다.

  • 식별자
  • 번호
  • 화이트 스페이스
  • 논평
  • EOF
  • {및 (와 같은 많은 기호는 자체 토큰 유형으로 계산

내가 겪고있는 문제는보다 일반적인 토큰 유형이 약간 임의적 인 것입니다. 예를 들어 왜 독자적인 토큰 유형과 IDENTIFIER를 STRING하고 있습니까? 문자열은 STRING_START + (IDENTIFIER | WHITESPACE) + STRING_START로 표시 될 수 있습니다.

이것은 또한 언어의 어려움과 관련이있을 수 있습니다. 예를 들어, 변수 선언은로 작성 {var-name var value}되고 배포됩니다 {var-name}. 그것은 것 같습니다 '{''}'자신의 토큰을해야하지만, var_name 반 및 VAR_VALUE 자격이 토큰 유형은, 또는 IDENTIFIER에서 이러한 것 모두 가을? 또한 VAR_VALUE에는 실제로 공백이 포함될 수 있습니다. 이후 공백 var-name은 선언에서 값의 시작을 나타내는 데 사용됩니다. 다른 공백은 값의 일부입니다. 이 공백이 자체 토큰이됩니까? 공백은이 문맥에서만 의미가 있습니다. 또한 {변수 선언의 시작이 아닐 수도 있습니다. 문맥에 따라 다릅니다 (다시 그 단어가 있습니다!). {:이름 선언을 시작하고{ 어떤 가치의 일부로도 사용될 수 있습니다.

내 언어는 들여 쓰기로 블록이 만들어 졌다는 점에서 파이썬과 비슷합니다. 파이썬은 들여 쓰기 (무엇으로 어느 정도 역할을 DEDENT 토큰을 생성하는 렉서 사용하는 방법에 대해 읽고 있었다 {}다른 언어을 많이 할 것을). 파이썬은 컨텍스트가 없다고 주장합니다. 즉, 적어도 어휘 분석기는 토큰을 만드는 동안 스트림의 어디에 있는지 신경 쓰지 않아야한다는 것을 의미합니다. 파이썬의 어휘 분석기는 어떻게 이전 문자에 대해 알지 못하고 특정 길이의 INDENT 토큰을 만들고 있다는 것을 어떻게 알 수 있습니까? 나도 이것을 알아야하기 때문에 묻습니다.

마지막 질문은 어리석은 질문입니다. 왜 어휘 분석기가 필요한가? 파서는 문자별로 이동하여 위치와 예상 위치를 파악할 수 있습니다. 어휘 분석기는 단순성의 이점을 추가합니까?


2
aheead로 가서 스캐너없는 파서를 작성해보십시오. 그것이 전혀 작동하지 않으면 (결과가 일부 파싱 알고리즘에 대해 너무 모호 할 수 있다고 생각합니다) 모든 "아래에 공백도 허용됩니다"및 "기다려 보았습니다. 식별자 또는 숫자? " 나는 경험에서 말합니다.

왜 커스텀 휠을 재발 명합니까? 맞춤식 어휘 분석기를 필요로하는 언어를 디자인하는 대신 LISP 나 FORTH와 같이 이미 내장 된 어휘 분석기와 함께 제공되는 기존 언어를 사용하는 것을 고려 했습니까?
John R. Strohm

2
학업 목적을위한 @ JohnR.Strohm. 어쨌든 언어 자체는 실제로 유용하지 않을 것입니다.
폭발 약

답변:


11

귀하의 질문 (마지막 단락 힌트로)은 실제로 어휘 분석기에 관한 것이 아니라 어휘 분석기와 파서 사이의 인터페이스의 올바른 디자인에 관한 것입니다. 알다시피, 어휘 분석기와 파서의 디자인에 관한 많은 책이 있습니다. Dick Grune파서 책 을 좋아 하지만 좋은 입문서가 아닐 수도 있습니다. 나는 AppelC 기반 서적 을 강력하게 싫어한다 . 왜냐하면 코드는 자신의 컴파일러에 유용하게 확장 될 수 없기 때문이다 (C를 척하는 결정에 내재 된 메모리 관리 문제 때문에 ML은 ML과 같다). 내 소개는 PJ Brown의 저서 였지만 좋은 일반적인 소개는 아닙니다 (특히 통역가에게는 꽤 좋지만). 그러나 당신의 질문으로 돌아갑니다.

답은 전방 또는 후방을 향한 제약 조건을 사용할 필요없이 어휘 분석기에서 할 수있는 한 많이하는 것입니다.

이것은 (언어의 세부 사항에 따라) 문자열을 "문자 뒤에 not-의 순서"와 다른 "문자"로 인식해야한다는 것을 의미합니다. 파서를 단일 단위로 반환하십시오. 이것에 대한 이유이지만 중요한 것은

  1. 이는 파서가 유지해야하는 상태의 양을 줄여 메모리 소비를 제한합니다.
  2. 이것은 lexer 구현이 기본 빌딩 블록을 인식하는 데 집중하고 개별 구문 요소가 프로그램을 빌드하는 데 사용되는 방법을 설명하도록 구문 분석기를 해제합니다.

종종 파서는 렉서로부터 토큰을받는 즉시 즉각적인 조치를 취할 수 있습니다. 예를 들어, IDENTIFIER가 수신 되 자마자 파서는 심볼 테이블 조회를 수행하여 심볼이 이미 알려져 있는지 확인할 수 있습니다. 구문 분석기가 문자열 상수를 QUOTE (IDENTIFIER SPACES) * QUOTE로 구문 분석하는 경우 관련이없는 많은 기호 테이블 조회를 수행하거나 구문 분석기의 구문 요소 트리보다 상위에있는 기호 테이블 조회를 올리게됩니다. 문자열을보고 있지 않다고 확신합니다.

내가 말하려는 것을 다시 말하지만, 어휘 분석기는 사물의 철자법과 사물의 구조에 대한 파서에 관심을 가져야한다.

문자열 모양에 대한 내 설명은 정규식과 비슷하게 보입니다. 이것은 우연의 일치가 아닙니다. 어휘 분석기는 정규식을 사용하는 작은 언어 ( Jon Bentley의 우수한 프로그래밍 진주 책 의 의미에서) 자주 구현됩니다 . 나는 텍스트를 인식 할 때 정규 표현식의 관점에서 생각하는 데 익숙합니다.

공백에 대한 질문과 관련하여 어휘 분석기에서 공백을 인식하십시오. 귀하의 언어가 꽤 자유로운 형식으로되어 있다면 WHITESPACE 토큰을 파서에 반환하지 마십시오. 파서를 버리기 만하면되기 때문에 파서의 생산 규칙은 기본적으로 소음으로 스팸 처리됩니다. 그들을 멀리.

공백이 구문 적으로 중요 할 때 공백을 처리하는 방법에 대한 의미는 언어에 대해 더 많이 알지 않고도 실제로 잘 작동 할 것입니다. 내 판단은 공백이 때때로 중요하고 때로는 그렇지 않은 경우를 피하고 따옴표와 같은 일종의 구분 기호를 사용하는 것입니다. 그러나 원하는 방식으로 언어를 디자인 할 수없는 경우이 옵션을 사용하지 못할 수 있습니다.

언어 구문 분석 시스템을 디자인하는 다른 방법이 있습니다. 분명히 lexer 및 파서 시스템을 결합 할 수있는 컴파일러 구성 시스템이 있습니다 (Java 버전의 ANTLR 이이 작업을 수행 한다고 생각합니다 ).하지만 결코 사용하지 않았습니다.

역사적 메모. 수십 년 전, 두 프로그램이 동시에 메모리에 맞지 않기 때문에 파서에 넘겨주기 전에 어휘 분석기가 가능한 한 많이하는 것이 중요했습니다. 렉서에서 더 많은 작업을 수행하면 파서가 더 똑똑해 지도록 더 많은 메모리를 사용할 수있게됩니다. 나는 몇 년 동안 Whitesmiths C Compiler 를 사용해 왔으며 올바르게 이해하면 64KB의 RAM (작은 모델의 MS-DOS 프로그램)에서만 작동하며 심지어 C의 변형을 번역했습니다. ANSI C에 매우 가깝습니다.


메모리 크기에 대한 좋은 역사적 메모는 작업을 우선 어휘 분석기와 파서로 나누는 이유 중 하나입니다.
stevegt

3

나는 당신의 마지막 질문을 할 것입니다. 사실은 어리석지 않습니다. 파서는 문자별로 복잡한 구성을 구축 할 수 있습니다. 내가 기억한다면, Harbison and Steele의 문법 ( "C-A reference manual")에는 단일 문자를 터미널로 사용하고 단일 문자에서 식별자, 문자열, 숫자 등을 비 터미널로 만드는 프로덕션이 있습니다.

공식 언어 관점에서, 정규식 기반 어휘 분석기는 "문자열 리터럴", "식별자", "숫자", "키워드"등으로 인식하고 분류 할 수있는 모든 것을 LL (1) 파서도 인식 할 수 있습니다. 따라서 파서 생성기를 사용하여 모든 것을 인식하는 데 이론적 인 문제는 없습니다.

알고리즘 관점에서 정규 표현식 인식기는 파서보다 훨씬 빠르게 실행할 수 있습니다. 인지 적 관점에서 프로그래머는 정규 표현식 어휘 분석기와 파서 생성기 작성 파서 간의 작업을 더 쉽게 분류 할 수 있습니다.

실질적인 고려 사항으로 인해 사람들이 별도의 어휘 분석기와 파서를 갖기로 결정했다.


그렇습니다. C 표준 자체는 내가 정확하게 기억하는 것처럼 Kernighan과 Ritchie의 두 판이했던 것과 같은 일을합니다.
James Youngman

3

문법을 실제로 이해하지 않고 어휘 분석기 / 파서를 작성하려는 것 같습니다. 일반적으로 사람들이 어휘 분석기와 파서를 작성할 때는 문법에 맞게 작성합니다. 어휘 분석기는 문법에 토큰을 반환해야하지만 파서는 규칙 / 비 터미널과 일치시키기 위해 토큰을 사용합니다 . 바이트 단위로 입력을 쉽게 구문 분석 할 수 있다면 어휘 분석기와 파서는 너무 과도 할 수 있습니다.

Lexers는 일을 더 단순하게 만듭니다.

문법 개요 : 문법은 일부 구문이나 입력의 모양에 대한 규칙 집합입니다. 예를 들어 다음은 장난감 문법입니다 (simple_command는 시작 기호 임).

simple_command:
 WORD DIGIT AND_SYMBOL
simple_command:
     addition_expression

addition_expression:
    NUM '+' NUM

이 문법은 다음을 의미합니다.-
simple_command는
A) WORD와 DIGIT, AND_SYMBOL로 구성됩니다 (이는 "정의 된 토큰"입니다)
B) "add_expression "(규칙 또는 "비 터미널")

addition_expression은
다음 과 같이 구성됩니다. NUM, '+', NUM (NUM)

따라서 simple_command는 "시작 기호"(시작 위치)이므로 토큰을받을 때 simple_command에 맞는지 확인합니다. 입력의 첫 번째 토큰이 WORD이고 다음 토큰이 DIGIT이고 다음 토큰이 AND_SYMBOL 인 경우 simple_command와 일치하여 조치를 취할 수 있습니다. 그렇지 않으면, 나는 그것을 simple_command의 다른 규칙 인 addition_expression과 일치 시키려고 노력할 것입니다. 따라서 첫 번째 토큰이 NUM 다음에 '+'다음에 NUM이면 simple_command와 일치하고 조치를 취합니다. 그런 것이 아니라면 구문 오류가 있습니다.

이것은 문법에 대한 매우 기초적인 소개입니다. 보다 철저한 이해를 위해이 위키 기사를 확인하고 컨텍스트없는 문법 튜토리얼을 웹에서 검색 하십시오 .

렉서 / 파서 배열을 사용하여 파서가 어떻게 보일지에 대한 예는 다음과 같습니다.

bool simple_command(){
   if (peek_next_token() == WORD){
       get_next_token();
       if (get_next_token() == DIGIT){
           if (get_next_token() == AND_SYMBOL){
               return true;
           } 
       }
   }
   else if (addition_expression()){
       return true;
   }

   return false;
}

bool addition_expression(){
    if (get_next_token() == NUM){
        if (get_next_token() == '+'){
             if (get_next_token() == NUM){
                  return true;
             }
        }
    }
    return false;
}

좋아, 그래서 코드는 추악하고 삼중 중첩 if 문을 권장하지 않습니다. 그러나 요점은 멋진 모듈 식 "get_next_token"및 "peek_next_token"함수를 사용하는 대신 문자 단위로 해당 작업을 수행하는 것을 상상해보십시오 . 진지하게, 그것을 쏴. 당신은 결과를 좋아하지 않을 것입니다. 위의 문법은 거의 모든 유용한 문법보다 약 30 배 덜 복잡합니다. 렉서 사용의 이점이 보입니까?

솔직히 말해서, 어휘 분석기와 파서는 세계에서 가장 기본적인 주제가 아닙니다. 먼저 문법을 읽고 이해 한 다음 어휘 분석기 / 파서에 대해 조금 읽고 나서 다이빙하는 것이 좋습니다.


문법 학습에 대한 권장 사항이 있습니까?
폭발 약

문법에 대한 기본적인 소개와 추가 학습을위한 몇 가지 제안을 포함하도록 답변을 편집했습니다. 문법은 컴퓨터 과학에서 매우 중요한 주제이므로 배울 가치가 있습니다.
Casey Patton

1

마지막 질문은 어리석은 질문입니다. 왜 어휘 분석기가 필요한가? 파서는 문자별로 이동하여 위치와 예상 위치를 파악할 수 있습니다.

이것은 바보가 아니라 진실 일뿐입니다.

그러나 실용성은 도구와 목표에 달려 있습니다. 예를 들어, 어휘 분석기없이 yacc를 사용하고 식별자에 유니 코드 문자를 허용하려면 모든 유효한 문자를 명시 적으로 열거하는 크고 못생긴 규칙을 작성해야합니다. 어휘 분석기에서는 문자가 문자 카테고리의 구성원인지 라이브러리 루틴에 요청할 수 있습니다.

어휘 분석기를 사용하거나 사용하지 않는 것은 당신의 언어와 문자 수준 사이에 추상화 수준을 갖는 것입니다. 요즘 문자 레벨은 바이트 레벨 위의 또 다른 추상화이며 비트 레벨 위의 추상화입니다.

마지막으로 비트 레벨을 파싱 할 수도 있습니다.


0
STRING_START + (IDENTIFIER | WHITESPACE) + STRING_START.

아뇨. 무엇에 대해 "("? 당신에 따르면, 그것은 유효한 문자열이 아닙니다. 그리고 탈출?

일반적으로 공백을 처리하는 가장 좋은 방법은 구분 토큰을 넘어 공백을 무시하는 것입니다. 많은 사람들이 매우 다른 공백을 선호하며 공백 규칙을 시행하는 것은 최선의 논쟁입니다.

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