run - yacc download
Entrada de cadena para flexionar lexer. (7)
Consulte esta sección del manual de Flex para obtener información sobre cómo escanear búferes en memoria, como cadenas.
Quiero crear un bucle de lectura-evaluación-impresión usando el analizador flex / bison. El problema es que el lexer generado por flexión quiere una entrada de tipo FILE * y me gustaría que fuera char *. ¿Hay alguna forma de hacer esto?
Una sugerencia ha sido crear un conducto, alimentarlo con la cadena, abrir el descriptor de archivo y enviarlo al lexer. Esto es bastante simple pero se siente complicado y no muy independiente de la plataforma. ¿Hay alguna manera mejor?
De otra manera, puede redefinir la función YY_INPUT en el archivo lex, y luego configurar su cadena a la entrada de LEX. Como a continuación:
#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); }
En su main.c, antes de escanear, primero debe configurar el búfer de lex:
set_lexbuf(your_string);
scanning...
Esto es lo que necesitaba hacer:
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;
}
no puedes acceder a la definición de tipo, lo que tiene sentido cuando piensas en ello.
La respuesta aceptada es incorrecta. Causará pérdidas de memoria.
Internamente, yy_scan_string llama a yy_scan_bytes que, a su vez, llama a yy_scan_buffer.
yy_scan_bytes asigna memoria para una COPIA del búfer de entrada.
yy_scan_buffer funciona directamente en el búfer suministrado.
Con las tres formas, DEBE llamar a yy_delete_buffer para liberar la información del estado del búfer flexible (YY_BUFFER_STATE).
Sin embargo, con yy_scan_buffer, evita la asignación / copia / libre interna del búfer interno.
El prototipo para yy_scan_buffer NO toma un carácter constante * y NO DEBE esperar que el contenido permanezca sin cambios.
Si asignó memoria para mantener su cadena, es responsable de liberarla DESPUÉS de que llame a yy_delete_buffer.
Además, no olvides que yywrap devuelve 1 (distinto de cero) cuando estés analizando SOLO esta cadena.
A continuación se muestra un ejemplo COMPLETO.
%%
<<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);
}
Las siguientes rutinas están disponibles para configurar buffers de entrada para escanear cadenas en memoria en lugar de archivos (como hace yy_create_buffer):
-
YY_BUFFER_STATE yy_scan_string(const char *str)
: escanea una cadena terminada en NUL` -
YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len)
: analiza los bytes len (incluso los NUL) que comienzan en los bytes de ubicación
Tenga en cuenta que ambas funciones crean, devuelven el correspondiente identificador YY_BUFFER_STATE (que debe eliminar con yy_delete_buffer () cuando termine con él), por lo que yylex () escanea una copia de la cadena o los bytes. Este comportamiento puede ser deseable ya que yylex () modifica el contenido del búfer que está escaneando).
Si quieres evitar la copia (y yy_delete_buffer) usando:
-
YY_BUFFER_STATE yy_scan_buffer(char *base, yy_size_t size)
muestra principal:
int main() {
yy_scan_buffer("a test string");
yylex();
}
aquí hay un pequeño ejemplo para usar bison / flex como un analizador dentro de su código cpp para analizar la cadena y cambiar un valor de cadena de acuerdo a esto (se eliminaron algunas partes del código por lo que podría haber partes irrelevantes allí.) 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
flex puede analizar char *
utilizando cualquiera de las tres funciones: yy_scan_string()
, yy_scan_buffer()
, y yy_scan_bytes()
(consulte la documentación ). Aquí hay un ejemplo de lo primero:
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;
}
Las declaraciones equivalentes para yy_scan_buffer()
(que requiere una cadena con doble terminación nula):
char string[] = "String to be parsed./0";
YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string));
Mi respuesta reitera parte de la información proporcionada por @dfa y @jlholland, pero ninguno de los códigos de sus respuestas parecía funcionar para mí.