flex/bison パーサーを使用して read-eval-print ループを作成したいと考えています。問題は、flex によって生成されたレクサーが FILE* 型の入力を必要としていて、それを char* にしたいということです。とにかくこれを行うことはありますか?
1 つの提案は、パイプを作成し、それに文字列を供給し、ファイル記述子を開き、レクサーに送信することです。これはかなり単純ですが、複雑で、プラットフォームにあまり依存していないように感じます。より良い方法はありますか?
flex/bison パーサーを使用して read-eval-print ループを作成したいと考えています。問題は、flex によって生成されたレクサーが FILE* 型の入力を必要としていて、それを char* にしたいということです。とにかくこれを行うことはありますか?
1 つの提案は、パイプを作成し、それに文字列を供給し、ファイル記述子を開き、レクサーに送信することです。これはかなり単純ですが、複雑で、プラットフォームにあまり依存していないように感じます。より良い方法はありますか?
次のルーチンは、ファイルの代わりにメモリ内文字列をスキャンするための入力バッファを設定するために使用できます (yy_create_buffer が行うように):
YY_BUFFER_STATE yy_scan_string(const char *str)
: NUL で終わる文字列をスキャンします`YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len)
: location バイトから始まる len バイト (場合によっては NUL を含む) をスキャンしますこれらの関数はどちらも対応する 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();
}
文字列などのメモリ内バッファをスキャンする方法については、Flex のマニュアルのこのセクションを参照してください。
flexは、 、
、および(ドキュメントchar *
を参照)の 3 つの関数のいずれかを使用して解析できます。最初の例を次に示します。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 を extern することはできません。これは、考えると理にかなっています。
これは、bison / flex を 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