문제

Flex/Bison Parser를 사용하여 Read-Eval-Print 루프를 만들고 싶습니다. 문제는 Flex 생성 Lexer가 유형 파일의 입력을 원하고 Char*가되기를 원합니다. 어쨌든 이것을 할 수 있습니까?

한 가지 제안은 파이프를 만들고 문자열을 공급하고 파일 디스크립터를 열고 Lexer로 보내는 것입니다. 이것은 상당히 간단하지만 복잡하고 플랫폼 독립적이지 않다고 느낍니다. 더 좋은 방법이 있습니까?

도움이 되었습니까?

해결책

다음 루틴은 파일 대신 메모리 문자열을 스캔하기위한 입력 버퍼를 설정하는 데 사용할 수 있습니다 (YY_CREATE_BUFFER와 같이).

  • YY_BUFFER_STATE yy_scan_string(const char *str): NUL 용어 문자열을 스캔합니다
  • YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len): 위치 바이트에서 시작하는 Len Bytes (가능할 수있는 숫자 포함) 스캔

이 기능 모두 생성된다. 해당 yy_buffer_state 핸들 (yy_delete_buffer ()로 삭제해야 함)를 반환한다. 따라서 yylex ()는 문자열 또는 바이트의 사본을 스캔한다. yylex ()가 스캔하는 버퍼의 내용을 수정하기 때문에이 동작이 바람직 할 수 있습니다).

다음을 사용하여 사본 (및 yy_delete_buffer)을 피하려면 :

  • YY_BUFFER_STATE yy_scan_buffer(char *base, yy_size_t size)

샘플 메인 :

int main() {
    yy_scan_buffer("a test string");
    yylex();
}

다른 팁

보다 이번 장 플렉스 매뉴얼의 설명서와 같은 메모리 버퍼를 스캔하는 방법에 대한 정보.

몸을 풀다 구문 분석 할 수 있습니다 char * 세 가지 기능 중 하나 사용 : yy_scan_string(), yy_scan_buffer(), 그리고 yy_scan_bytes() (참조 선적 서류 비치). 다음은 첫 번째 예입니다.

typedef struct yy_buffer_state * YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_string(char * str);
extern void yy_delete_buffer(YY_BUFFER_STATE buffer);

int main(){
    char string[] = "String to be parsed.";
    YY_BUFFER_STATE buffer = yy_scan_string(string);
    yyparse();
    yy_delete_buffer(buffer);
    return 0;
}

동등한 진술 yy_scan_buffer() (이중 무일하게 종결 된 문자열이 필요합니다) :

char string[] = "String to be parsed.\0";
YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string));

내 대답은 @DFA와 @Jlholland가 제공 한 정보 중 일부를 되풀이하지만 응답의 코드 중 어느 것도 저에게도 효과가없는 것 같습니다.

다음은 다음과 같습니다.

extern yy_buffer_state;
typedef yy_buffer_state *YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_buffer(char *, size_t);

int main(int argc, char** argv) {

  char tstr[] = "line i want to parse\n\0\0";
  // note yy_scan_buffer is is looking for a double null string
  yy_scan_buffer(tstr, sizeof(tstr));
  yy_parse();
  return 0;
}

당신은 typedef를 외부 할 수 없으며, 그것에 대해 생각할 때 의미가 있습니다.

허용 된 답변이 잘못되었습니다. 메모리 누출이 발생합니다.

내부적으로 yy_scan_string은 yy_scan_bytes를 호출하여 yy_scan_buffer를 호출합니다.

yy_scan_bytes는 입력 버퍼의 사본에 대한 메모리를 할당합니다.

yy_scan_buffer는 제공된 버퍼에서 직접 작동합니다.

세 가지 양식을 모두 사용하면 yy_delete_buffer에 전화하여 Flex 버퍼 상태 정보 (yy_buffer_state)를 제거해야합니다.

그러나 YY_SCAN_BUFFER를 사용하면 내부 할당/복사/내부 버퍼가없는 것을 피하십시오.

yy_scan_buffer의 프로토 타입은 const char*를 사용하지 않으며 내용이 변경되지 않도록 기대해서는 안됩니다.

문자열을 보유하기 위해 메모리를 할당 한 경우 yy_delete_buffer로 전화 한 후에는 해방할 책임이 있습니다.

또한이 문자열 만 구문 분석 할 때 yywrap return 1 (0이 아닌)을 잊지 마십시오.

아래는 완전한 예입니다.

%%

<<EOF>> return 0;

.   return 1;

%%

int yywrap()
{
    return (1);
}

int main(int argc, const char* const argv[])
{
    FILE* fileHandle = fopen(argv[1], "rb");
    if (fileHandle == NULL) {
        perror("fopen");
        return (EXIT_FAILURE);
    }

    fseek(fileHandle, 0, SEEK_END);
    long fileSize = ftell(fileHandle);
    fseek(fileHandle, 0, SEEK_SET);

    // When using yy_scan_bytes, do not add 2 here ...
    char *string = malloc(fileSize + 2);

    fread(string, fileSize, sizeof(char), fileHandle);

    fclose(fileHandle);

    // Add the two NUL terminators, required by flex.
    // Omit this for yy_scan_bytes(), which allocates, copies and
    // apends these for us.   
    string[fileSize] = '\0';
    string[fileSize + 1] = '\0';

    // Our input file may contain NULs ('\0') so we MUST use
    // yy_scan_buffer() or yy_scan_bytes(). For a normal C (NUL-
    // terminated) string, we are better off using yy_scan_string() and
    // letting flex manage making a copy of it so the original may be a
    // const char (i.e., literal) string.
    YY_BUFFER_STATE buffer = yy_scan_buffer(string, fileSize + 2);

    // This is a flex source file, for yacc/bison call yyparse()
    // here instead ...
    int token;
    do {
        token = yylex(); // MAY modify the contents of the 'string'.
    } while (token != 0);

    // After flex is done, tell it to release the memory it allocated.    
    yy_delete_buffer(buffer);

    // And now we can release our (now dirty) buffer.
    free(string);

    return (EXIT_SUCCESS);
}

다른 방향으로는 Lex 파일에서 기능 yy_input 기능을 재정의 한 다음 문자열을 Lex의 입력으로 설정할 수 있습니다. 아래:

#undef YY_INPUT
#define YY_INPUT(buf) (my_yyinput(buf))

char my_buf[20];

void set_lexbuf(char *org_str)
{  strcpy(my_buf, org_str);  }

void my_yyinput (char *buf)
{  strcpy(buf, my_buf);      } 

Main.c에서 스캔하기 전에 Lex의 버퍼를 먼저 설정해야합니다.

set_lexbuf(your_string);
scanning...

다음은 문자열을 구문 분석하기 위해 CPP 코드 내부의 구문 분석기로 Bison / Flex를 사용하는 작은 예입니다. 그에 따라 문자열 값을 변경하는 문자열 값을 변경하는 작은 예입니다 (코드의 일부 부분이 제거되어 관련없는 부품이있을 수 있습니다.) Parser.y : y :

%{
#include "parser.h"
#include "lex.h"
#include <math.h> 
#include <fstream>
#include <iostream> 
#include <string>
#include <vector>
using namespace std;
 int yyerror(yyscan_t scanner, string result, const char *s){  
    (void)scanner;
    std::cout << "yyerror : " << *s << " - " << s << std::endl;
    return 1;
  }
    %}

%code requires{
#define YY_TYPEDEF_YY_SCANNER_T 
typedef void * yyscan_t;
#define YYERROR_VERBOSE 0
#define YYMAXDEPTH 65536*1024 
#include <math.h> 
#include <fstream>
#include <iostream> 
#include <string>
#include <vector>
}
%output "parser.cpp"
%defines "parser.h"
%define api.pure full
%lex-param{ yyscan_t scanner }
%parse-param{ yyscan_t scanner } {std::string & result}

%union {
  std::string *  sval;
}

%token TOKEN_ID TOKEN_ERROR TOKEN_OB TOKEN_CB TOKEN_AND TOKEN_XOR TOKEN_OR TOKEN_NOT
%type <sval>  TOKEN_ID expression unary_expression binary_expression
%left BINARY_PRIO
%left UNARY_PRIO
%%

top:
expression {result = *$1;}
;
expression:
TOKEN_ID  {$$=$1; }
| TOKEN_OB expression TOKEN_CB  {$$=$2;}
| binary_expression  {$$=$1;}
| unary_expression  {$$=$1;}
;

unary_expression:
 TOKEN_NOT expression %prec UNARY_PRIO {result =  " (NOT " + *$2 + " ) " ; $$ = &result;}
;
binary_expression:
expression expression  %prec BINARY_PRIO {result = " ( " + *$1+ " AND " + *$2 + " ) "; $$ = &result;}
| expression TOKEN_AND expression %prec BINARY_PRIO {result = " ( " + *$1+ " AND " + *$3 + " ) "; $$ = &result;} 
| expression TOKEN_OR expression %prec BINARY_PRIO {result = " ( " + *$1 + " OR " + *$3 + " ) "; $$ = &result;} 
| expression TOKEN_XOR expression %prec BINARY_PRIO {result = " ( " + *$1 + " XOR " + *$3 + " ) "; $$ = &result;} 
;

%%

lexer.l : 

%{
#include <string>
#include "parser.h"

%}
%option outfile="lex.cpp" header-file="lex.h"
%option noyywrap never-interactive
%option reentrant
%option bison-bridge

%top{
/* This code goes at the "top" of the generated file. */
#include <stdint.h>
}

id        ([a-zA-Z][a-zA-Z0-9]*)+
white     [ \t\r]
newline   [\n]

%%
{id}                    {    
    yylval->sval = new std::string(yytext);
    return TOKEN_ID;
}
"(" {return TOKEN_OB;}
")" {return TOKEN_CB;}
"*" {return TOKEN_AND;}
"^" {return TOKEN_XOR;}
"+" {return TOKEN_OR;}
"!" {return TOKEN_NOT;}

{white};  // ignore white spaces
{newline};
. {
return TOKEN_ERROR;
}

%%

usage : 
void parse(std::string& function) {
  string result = "";
  yyscan_t scanner;
  yylex_init_extra(NULL, &scanner);
  YY_BUFFER_STATE state = yy_scan_string(function.c_str() , scanner);
  yyparse(scanner,result);
  yy_delete_buffer(state, scanner);
  yylex_destroy(scanner);
  function = " " + result + " ";  
}

makefile:
parser.h parser.cpp: parser.y
    @ /usr/local/bison/2.7.91/bin/bison -y -d parser.y


lex.h lex.cpp: lexer.l
    @ /usr/local/flex/2.5.39/bin/flex lexer.l

clean:
    - \rm -f *.o parser.h parser.cpp lex.h lex.cpp

Libmatheval에는이 재미있는 코드가 있습니다.

/* Redefine macro to redirect scanner input from string instead of
 * standard input.  */
#define YY_INPUT( buffer, result, max_size ) \
{ result = input_from_string (buffer, max_size); }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top