c फ्लेक्स लेक्सर को स्ट्रिंग इनपुट




bison yacc (7)

इन-मेमोरी बफ़र्स को स्कैन करने के तरीके के बारे में जानकारी के लिए फ्लेक्स के मैनुअल के इस खंड को देखें।

मैं फ्लेक्स / बाइसन पार्सर का उपयोग करके एक रीड-इवल-प्रिंट लूप बनाना चाहता हूं। परेशानी यह है कि, फ्लेक्स जेनरेट होने वाला लेक्सर टाइप FILE * का इनपुट चाहता है और मैं चाहूंगा कि यह चार * हो। क्या इसे करने का कोई तरीका है?

एक सुझाव पाइप बनाने के लिए दिया गया है, इसे स्ट्रिंग को खिलाएं और फ़ाइल डिस्क्रिप्टर खोलें और लेक्सर को भेजें। यह काफी सरल है लेकिन यह जटिल लगता है और स्वतंत्र रूप से बहुत मंच नहीं है। क्या कोई बेहतर तरीका है?


यहाँ पार्सिंग स्ट्रिंग के लिए अपने cpp कोड के अंदर एक पार्सर के रूप में बाइसन / फ्लेक्स का उपयोग करने और उसके अनुसार एक स्ट्रिंग मान को बदलने के लिए एक छोटा सा उदाहरण है (कोड के कुछ हिस्सों को हटा दिया गया था, इसलिए वहां अप्रासंगिक भाग हो सकते हैं।) parser'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); }

स्वीकृत उत्तर गलत है। यह मेमोरी लीक का कारण होगा।

आंतरिक रूप से, yy_scan_string yy_scan_bytes कॉल करता है, जो बदले में, yy_scan_buffer कहता है।

yy_scan_bytes इनपुट बफर के एक COPY के लिए मेमोरी आवंटित करता है।

yy_scan_buffer सीधे दिए गए बफर पर काम करता है।

सभी तीन रूपों के साथ, आप फ्लेक्स बफर-स्टेट जानकारी (YY_BUFFER_STATE) को मुक्त करने के लिए yy_delete_buffer को कॉल करें।

हालाँकि, yy_scan_buffer के साथ, आप आंतरिक आवंटन / कॉपी / आंतरिक बफर से मुक्त होने से बचते हैं।

Yy_scan_buffer के लिए प्रोटोटाइप एक कास्ट चार * नहीं लेता है और आपको सामग्री के अपरिवर्तित रहने की उम्मीद नहीं है।

यदि आपने अपनी स्ट्रिंग को रखने के लिए मेमोरी आवंटित की है, तो आप इसे yy_delete_buffer कहते हैं, इसे मुक्त करने के लिए जिम्मेदार हैं।

इसके अलावा, जब आप इस स्ट्रिंग को पार्स कर रहे हैं तो yywrap 1 (गैर-शून्य) वापस करना न भूलें।

नीचे एक पूरा उदाहरण है।

%%

<<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);
}

फ्लेक्स char * कार्यों में से किसी एक का उपयोग करके 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 और @jhholland द्वारा प्रदान की गई कुछ जानकारी को दोहराता है, लेकिन उनके जवाबों में से कोई भी कोड मेरे लिए काम नहीं करता था।


यहाँ मुझे क्या करना है:

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;
}

आप टाइप्डफ को बाहर नहीं कर सकते हैं, जो जब आप इसके बारे में सोचते हैं तो समझ में आता है।


दूसरे तरीके से, आप फ़ंक्शन को 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);      } 

आपके मुख्य.c में, स्कैनिंग से पहले, आपको पहले lex का बफर सेट करने की आवश्यकता है:

set_lexbuf(your_string);
scanning...