12
votes

i am working in a small language/IDE. And I need to know how to configure flex and bison to work together but without using any global or static variable. I need to pass to bison my AST pointer. I also need that bison pass my AST to flex as well. It's a thread environment but i dont need any thread sync. And I need a separete yylineno variable for each yyparse() call. I read about %define api.pure , %parse-param and %option reentrant. But i don't know how to put them to work together... thx in advance...

I tried this:

scanner.l:

%{

#include <iostream>
#include <sstream>
#include "parser.tab.h"
#define YY_DECL extern "C" int yylex(YYSTYPE * lvalp, yyscan_t scanner)
extern void yyerror(yyscan_t scanner, NBloco * bloco, const char *s);

%}

%option noyywrap
%option yylineno
%option reentrant 
%option bison-bridge

%%
//...scanner code

parser.y:

%{
#include <iostream>
#include "AST.h"

#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif

extern "C" int yylex(YYSTYPE * lvalp, yyscan_t scanner);
extern "C" FILE *yyin;
extern int yylineno;
void yyerror(yyscan_t scanner, NBloco * bloco, const char *s);
%}

%union{ 
//union code
}

%define api.pure full
%lex-param   { yyscan_t scanner }
%parse-param { yyscan_t scanner }
%parse-param { NBlock* block}

//tokens...
//types...

%%

//parser code...

And I got this:

parser.y:13:22: warning: 'yylex' initialized and declared 'extern' [enabled by default] extern "C" int yylex(YYSTYPE * lvalp, yyscan_t scanner);


parser.y:13:22: error: 'YYSTYPE' was not declared in this scope


parser.y:13:32: error: 'lvalp' was not declared in this scope extern "C" int yylex(YYSTYPE * lvalp, yyscan_t scanner);


parser.y:13:48: error: expected primary-expression before 'scanner'
 extern "C" int yylex(YYSTYPE * lvalp, yyscan_t scanner);

                                            ^
parser.y:13:55: error: expression list treated as compound expression in initializer [-fpermissive]
 extern "C" int yylex(YYSTYPE * lvalp, yyscan_t scanner);

                                                   ^
parser.tab.c: In function 'int yyparse(yyscan_t, NBloco*)':
parser.tab.c:932:39: error: 'yylex' cannot be used as a function
 # define YYLEX yylex (&yylval, scanner)


parser.tab.c:1618:16: note: in expansion of macro 'YYLEX'
       yychar = YYLEX;

// more errors...

I also to fix the yylineno to be per file... i am basing on http://benlynn.blogspot.com.br/2013/12/reentrant-parsers-with-flex-and-bison.html and http://www.lemoda.net/c/reentrant-parser/index.html

1
Why not just pass an instance created in the main() scope?πάντα ῥεῖ
i added it but i need bison pass it to flex as well...Bruno Lebtag
Maybe, some small code sample might help to illustrate about your problems. I don't think it's necessary to post the full code here, just the relevant parts, you have problems with in particular.πάντα ῥεῖ
Multiple yyparse calls are to support includes? Then did you look at "Multiple Input Buffers" flex.sourceforge.net/manual/…? Also why AST to FLEX? Your scanner depends on AST?jbp
No. it doesnt support includes. it's a IDE with multiple tabs and i want to support to execute multiple tabs at the same time. And well, the flex doesnt really need AST...Bruno Lebtag

1 Answers

19
votes

First, here's a C reentrant flex parser and pure bison example that parses a grammar that matches the following:

()
(())
(()())

()()(())()()

lexer.l

%option bison-bridge
%option bison-locations
%option ecs
%option nodefault
%option noyywrap
%option reentrant
%option stack
%option warn
%option yylineno

%{
  #include "parser.h"
%}

%%

"(" { return (LPAREN); }
")" { return (RPAREN); }

[ \f\r\t\v\n]+ /* eat whitespace */

%%

/* don't use lexer.l for code, organize it logically elsewhere */

parser.y

%define parse.error verbose
%define api.pure true
%locations
%token-table
%glr-parser
%lex-param {void *scanner}
%parse-param {void *scanner}

%{
/* your top code here */
%}

%union {
  int value; // or whatever else here
}

%token LPAREN
%token RPAREN

%%

document
    : exprs

exprs
    : %empty
    | expr exprs

expr
    : parens

parens
    : LPAREN exprs RPAREN


%%

int
yyerror(YYLTYPE *locp, char *msg) {
  if (locp) {
    fprintf(stderr, "parse error: %s (:%d.%d -> :%d.%d)\n",
                    msg,
                    locp->first_line, locp->first_column,
                    locp->last_line,  locp->last_column
    );
    /* todo: add some fancy ^^^^^ error handling here */
  } else {
    fprintf(stderr, "parse error: %s\n", msg);
  }
  return (0);
}

main.c

#include "parser.h"
#include "lexer.h"

int
main(int argc, char **argv) {
  int result;
  yyscan_t scanner;

  yylex_init(&scanner);
  result = (yyparse(scanner));
  yylex_destroy(scanner);
  return (result);
}

Building

flex --header-file=lexer.h --outfile=lexer.c lexer.l
bison --output-file=parser.c --defines=parser.h --warnings=all --feature=all parser.y
cc lexer.c parser.c main.c -o parser
./parser

Note: OSX's built-in bison is outdated, so install 3.x:

brew install bison

And then run it like /usr/local/opt/bison/bin/bison ....

Now, to migrate to C++

  • copy .l to .lxx and .y to .yxx
  • change names for output files to *.cxx and *.hxx respectively.

lexer.lxx

  • add %option c++
  • remove reentrant, bison-bridge and bison-locations
  • change all tokens like the following: LPAREN to yy::parser::token::LPAREN

parser.yxx

  • add %skeleton "lalr1.cc"
  • remove api.pure
  • remove yyerror

main.cxx

  • rewrite it for C++

Hooking up the lexer and parser objects is an exercise for the reader.

See also: https://github.com/bingmann/flex-bison-cpp-example but beware it uses the old bison 2.x interfaces.

GNU Bison 3.x C++ Example docs