문제

복잡한 언어에서 특정 문법 요소를 얻고 싶기 때문에 flex와 bison을 사용하여 필터를 만들려고 합니다.내 계획은 flex + bison을 사용하여 문법을 인식하고 관심 있는 요소의 위치를 ​​버리는 것입니다.(그런 다음 스크립트를 사용하여 덤프된 위치에 따라 텍스트를 가져옵니다.)

저는 flex가 bison-locations라는 들소 기능을 지원할 수 있다는 것을 알았습니다. 하지만 정확히 어떻게 작동하는지 알아냈습니다.Flex 문서에서 예제를 시도했는데 yylloc이 Flex에 의해 자동으로 설정되지 않은 것 같습니다. 항상 다음과 같은 메시지가 나타납니다. (1,0)-(1,0).Flex가 각 토큰의 위치를 ​​자동으로 계산할 수 있나요?그렇지 않은 경우 구현하도록 정의된 인터페이스 기능은 무엇입니까?어떤 예가 있나요?

도구와 관련된 더 나은 솔루션이 있습니까?

안부, 케빈

편집하다:

이제 yylex의 인터페이스는 다음과 같습니다.

int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );

bison 매뉴얼은 yylloc_param을 올바르게 설정하기 위해 어휘분석기가 어떻게 구현되어야 하는지를 지정하지 않습니다.나에게는 각 토큰의 열 번호를 수동으로 추적하는 것이 어렵습니다.

도움이 되었습니까?

해결책

섹션을 살펴보세요 Bison 매뉴얼 3.6 - 위치를 어느 정도 자세히 다루는 것 같습니다.Flex 매뉴얼에서 찾은 내용과 결합하면 충분할 수 있습니다.

다른 팁

재진입 또는 순수 파서를 사용했기 때문에 yylex 선언이 변경되었을 수 있습니다.웹상의 많은 문서에서는 들소 위치가 작동하도록 하려면 필요하다고 제안하지만 필수는 아닙니다.

줄 번호도 필요했고 그 점에서 Bison 문서가 혼란스럽다는 것을 알았습니다.간단한 해결책(전역 var yylloc 사용):Bison 파일에 %locations 지시문을 추가하세요.

%{
...
%}
%locations
...
%%
...

귀하의 어휘분석기에서:

%{
...
#include "yourprser.tab.h"  /* This is where it gets the definition for yylloc from */
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
...
%%
...

YY_USER_ACTION 매크로는 각 토큰 작업 전에 "호출"되고 yylloc을 업데이트합니다.이제 다음과 같이 @N/@$ 규칙을 사용할 수 있습니다.

statement : error ';'   { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }

, 또는 yylloc 전역 변수를 사용하세요.

void yyerror(char *s)
{
  fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}

나는 Shlomi의 대답을 좋아합니다.

또한 열 위치 업데이트도 찾고 있었습니다.설립하다 http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.html Shlomi의 답변을 읽은 후에 더 이해가 되었습니다.

불행히도 해당 페이지에 yylloc에 ​​대한 오타가 있습니다.아래에서 조금 단순화했습니다.

파서에 다음을 추가하세요.

%locations

귀하의 어휘분석기에서:

%{

#include "parser.tab.h"

int yycolumn = 1;

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \
    yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \
    yycolumn += yyleng; \
    yylval.str = strdup(yytext);

%}

%option yylineno

열을 엄격하게 추적하지 않고 계속 증가하는 열 위치에 문제가 있을 수 있습니다.그것은 단지 나의 무지이며 누군가에게 혼란을 주었다면 사과드립니다.저는 현재 열을 사용하여 파일 문자 수를 유지하고 있습니다. 제 경우에는 열 위치보다 더 유익합니다.

도움이 되길 바랍니다.

어느 것도 아니다 bison ...도 아니다 flex 업데이트 yylloc 자동으로 수행되지만 요령을 알고 있다면 직접 수행하는 것은 실제로 어렵지 않습니다.

구현의 비결 yylloc 지원은 그렇다 하더라도 yyparse() 선언하다 yylloc, 결코 변경되지 않습니다.즉, 수정하면 yylloc 어휘분석기를 한 번 호출하면 다음 호출에서도 동일한 값을 찾을 수 있습니다.따라서, yylloc 마지막 토큰의 위치가 포함됩니다.마지막 토큰의 끝은 현재 토큰의 시작과 동일하므로 이전 토큰을 사용할 수 있습니다. yylloc 새 값을 결정하는 데 도움이 됩니다.

다시 말해서, yylex() 해서는 안 된다 계산하다 yylloc;그래야 한다 업데이트 yylloc.

업데이트하려면 yylloc, 먼저 last_ 가치를 first_, 을 선택한 다음 last_ 방금 일치하는 토큰의 길이를 반영하는 값입니다.(이것은 아니다 strlen() 토큰의;그것은 행과 열의 길이입니다.) 우리는 이것을 다음과 같이 할 수 있습니다. YY_USER_ACTION 어휘분석기 작업이 수행되기 직전에 호출되는 매크로;규칙이 일치하지만 값을 반환하지 않는 경우(예: 공백이나 주석을 건너뛰는 규칙) 실제 토큰의 시작 부분에 포함되지 않고 해당 비토큰의 위치를 ​​건너뜁니다. 위치 추적이 부정확해지는 방식으로 분실되었습니다.

다음은 재진입 파서용 버전입니다.재진입이 불가능한 파서에 대해 이를 교체하여 수정할 수 있습니다. -> 연산자 .:

#define YY_USER_ACTION \
    yylloc->first_line = yylloc->last_line; \
    yylloc->first_column = yylloc->last_column; \
    for(int i = 0; yytext[i] != '\0'; i++) { \
        if(yytext[i] == '\n') { \
            yylloc->last_line++; \
            yylloc->last_column = 0; \
        } \
        else { \
            yylloc->last_column++; \
        } \
    }

원하는 경우 대신 해당 코드를 함수에 넣고 매크로가 함수를 호출하도록 할 수 있지만 두 기술은 동일합니다.

줄 번호 유지에만 관심이 있다면 Shomi의 답변이 가장 간단한 솔루션입니다.그러나 열 번호도 원하는 경우 해당 번호를 추적해야 합니다.

이를 수행하는 한 가지 방법은 다음을 추가하는 것입니다. yycolumn = 1 (David Elson의 답변에서 제안한 대로) 개행 문자가 표시되는 모든 곳에서 규칙을 사용하지만 개행 문자가 표시될 수 있는 모든 위치(공백, 주석 등...)를 추적하려는 경우 대안은 다음을 검사하는 것입니다. yytext 모든 작업 시작 시 버퍼링:

static void update_loc(){
  static int curr_line = 1;
  static int curr_col  = 1;

  yylloc.first_line   = curr_line;
  yylloc.first_column = curr_col;

  {char * s; for(s = yytext; *s != '\0'; s++){
    if(*s == '\n'){
      curr_line++;
      curr_col = 1;
    }else{
      curr_col++;
    }
  }}

  yylloc.last_line   = curr_line;
  yylloc.last_column = curr_col-1;
}

#define YY_USER_ACTION update_loc();

마지막으로 한 가지 주목해야 할 점은 일단 열 번호를 손으로 추적하기 시작하면 동일한 위치에서 줄 번호를 추적하고 Flex를 사용하는 데 신경 쓰지 않아도 된다는 것입니다. yylineno 옵션.

그래서 나는 이것을 "작동"하도록 만들었지만 몇 가지 추가 단계를 수행했습니다(여기서 간과했을 수도 있습니다...그렇다면 사과드립니다):

  1. ~ 안에 파서.y, 나는 이렇게 말해야 했습니다:

    #define YYLEX_PARAM &yylval, &yylloc
    

    심지어 %locations 그리고 bison --locations, 데이터를 전달하도록 합니다.

  2. ~ 안에 lexer.l 나는 사용해야했다 -> 대신에 . ~을 위한 yylloc

  3. 또한 lexer.l, 작업에서 열을 재설정했습니다.

    [\n] { yycolumn = 1; }
    

분명히 조금 더 복잡합니다. \r 등등, 그러나 적어도 나는 그것을 작동하게 만들었습니다.

나는 그것을 작동시킬 수 있었다고 생각합니다(신용은 들소 매뉴얼 작성자에게 있음) ltcalc 어휘 분석기).기본적으로 bison은 다음을 포함하는 yylloc을 생성합니다.

{ first_line, first_column , last_line , last_column }

어휘 분석기에서 해당 값만 업데이트하면 됩니다.전 :

[ \t]     { ++yylloc.last_column; }
[\n]      { yyloc.last_column = 0; return EOL; }
[a-zA-Z]+ { 
            yylloc.last_column += strlen(yytext);
            return IDENTIFIER;
          }

이제 Bison에서 해당 필드를 검색하려면 다음을 수행합니다.

statement : IDENTIFIER '=' expression 
            { printf("%d - %d\n", @1.last_line, @1.last_column); }

기본적으로 이러한 필드는 1로 초기화됩니다. 열 필드를 0으로 초기화해야 합니다. 그렇지 않으면 잘못된 열이 보고됩니다.

Shlomi의 답변에 추가 사항 :

재진입 파서를 생성하기 위해 bison에서 %define api.pure를 사용하는 경우 flex에서 %option bison-locations도 지정해야 합니다.이는 재진입 파서에서 yylloc이 전역 변수가 아니고 어휘 분석기에 전달되어야 하기 때문입니다.

따라서 파서에서 다음을 수행합니다.

%define api.pure
%locations

어휘분석기에서:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top