Pregunta

Quiero crear un bucle de lectura-eval-impresión utilizando analizador de flex / bisonte. El problema es que el flex generó lexer quiere de entrada de tipo FILE * y me gustaría que sea char *. ¿Hay alguna forma de hacer esto?

Una sugerencia ha sido la creación de una tubería, lo alimentan la cadena y abrir el descriptor de archivo y enviar al léxico. Esto es bastante simple, pero se siente muy complicado y no plataforma independiente. ¿Hay una mejor manera?

¿Fue útil?

Solución

Las siguientes funciones están disponibles para la creación de buffers de entrada para el análisis de cadenas en memoria en lugar de archivos (como se hace yy_create_buffer):

  • YY_BUFFER_STATE yy_scan_string(const char *str): escanea un string` terminado en NUL
  • YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len): exploraciones len bytes (incluyendo posiblemente NULs) a partir de bytes ubicación

Tenga en cuenta que estas dos funciones crean, devuelve un recurso YY_BUFFER_STATE correspondiente (que se debe eliminar con yy_delete_buffer () cuando termine con él) por lo yylex () escanear una copia de la cadena o bytes. Este comportamiento puede ser deseable desde yylex () modifica el contenido de la memoria intermedia es de barrido).

Si desea 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();
}

Otros consejos

esta sección de Flex manual para obtener información sobre cómo escanear buffers de memoria, como cadenas.

flex puede analizar char * usando cualquiera de las tres funciones: yy_scan_string(), yy_scan_buffer() y yy_scan_bytes() (véase el documentación ). He aquí un ejemplo de la primera:

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

Los estados equivalentes para yy_scan_buffer() (que requiere una cadena doblemente terminada en nulo):

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 código de sus respuestas parecía estar funcionando para mí.

Esto es lo que tenía que 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 se puede extern el typedef, que tiene sentido cuando se piensa en ello.

La respuesta aceptada es incorrecta. Esto causará pérdidas de memoria.

A nivel interno, yy_scan_string llama yy_scan_bytes el cual, a su vez, las llamadas yy_scan_buffer.

yy_scan_bytes asigna memoria para una copia de la memoria intermedia de entrada.

yy_scan_buffer trabaja directamente sobre el tampón suministrado.

Con las tres formas, debe llamar yy_delete_buffer para liberar la información estado-tapón flex (YY_BUFFER_STATE).

Sin embargo, con yy_scan_buffer, se evita la asignación interna / copiar / libre del buffer interno.

El prototipo para yy_scan_buffer hace falta ser un const char * Y no deben esperar que los contenidos se mantienen sin cambios.

Si asigna memoria para contener la cadena, usted es responsable de liberándola después de llamar yy_delete_buffer.

Además, no se olvide de tener yywrap de retorno 1 (distinto de cero) cuando estás analizar sólo 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);
}

Otro vías, se puede redefinir YY_INPUT función en el archivo de Lex, y luego fijar 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 la digitalización, debe configurar primero la memoria intermedia del lex:

set_lexbuf(your_string);
scanning...

He aquí un pequeño ejemplo para el uso de bisonte / Flex como un analizador dentro de su código de CPP para el análisis de cadenas y el cambio de un valor de cadena de acuerdo con ella (Pocas partes del código se eliminaron por lo que podría ser 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

Hay un código de gracioso en 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); }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top