Dylan_Larkin Dylan_Larkin - 1 month ago 27
C++ Question

Flex, Bison, C++ all in Xcode

I'm working through Problems with reentrant Flex and Bison. It compiles and runs just fine on my machine. What I want to do though is make use of C++ STL. Anytime I try to include a CPP header, it says it can't be found. There are only a handful of questions about this on Goog. Does anyone have a working example of this sort of setup, or a solution I might implement?

Any help would be greatly appreciated.

Thanks!

EDIT So for one reason or another, I have to add the include path of any headers in the build settings. Must be due to the custom makefile of this person's example. It's above my pay-grade. Anyway, I can now use STL libraries inside of main.

WHAT I REALLY WANT TO DO IS USE FLEX/BISON WITH CPP, AND IF I TRY TO INCLUDE STL HEADERS ANYWHERE BUT MAIN, I GET ERROR "HEADER NOT FOUND".

I can include C-headers just fine, though.

Answer

Here's answer from the author of another answer in the linked topic.

I have adapted that my example to work with C++.

The key points are:

  • I am using recent Flex / Bison: brew install flex and brew install bison. Not sure if the same will work with default OSX/Xcode's flex/bison.

  • Generated flex/bison files should have C++ extensions (lexer.[hpp|mm], parser.[hpp|mm]) for Xcode to pick up the C++ code.

  • There is a Xcode's Build Phase that runs Make.

All the relevant files follow below but I recommend you to check out the example project.


main.mm's code is

#include "parser.hpp"
#include "lexer.hpp"

extern YY_BUFFER_STATE yy_scan_string(const char * str);
extern void yy_delete_buffer(YY_BUFFER_STATE buffer);

ParserConsumer *parserConsumer = [ParserConsumer new];

char input[] = "RAINBOW UNICORN 1234 UNICORN";

YY_BUFFER_STATE state = yy_scan_string(input);
yyparse(parserConsumer);
yy_delete_buffer(state);

Lexer.lm:

%{

#include "ParserConsumer.h"
#include "parser.hpp"

#include <iostream>
#include <cstdio>

int yylex(void);
void yyerror(id <ParserConsumer> consumer, const char *msg);

%}

%option header-file = "./Parser/Generated Code/lexer.hpp"
%option outfile     = "./Parser/Generated Code/lexer.mm"
%option noyywrap

NUMBER [0-9]+
STRING [A-Z]+
SPACE \x20

%%

{NUMBER} {
    yylval.numericValue = (int)strtoul(yytext, NULL, 10);

    std::cout << "Lexer says: Hello from C++\n";
    printf("[Lexer, number] %s\n", yytext);

    return Token_Number;
}

{STRING} {
    yylval.stringValue = strdup(yytext);

    printf("[Lexer, string] %s\n", yytext);

    return Token_String;
}

{SPACE} {
    // Do nothing
}

<<EOF>> {
    printf("<<EOF>>\n");

    return 0;
}

%%

void yyerror (id <ParserConsumer> consumer, const char *msg) {
    printf("%s\n", msg);

    abort();
}

Parser.ym:

%{

#include <iostream>
#include <cstdio>

#include "ParserConsumer.h"

#include "parser.hpp"
#include "lexer.hpp"

int yylex();
void yyerror(id <ParserConsumer> consumer, const char *msg);

%}

%output  "Parser/Generated Code/parser.mm"
%defines "Parser/Generated Code/parser.hpp"

  //%define api.pure full
%define parse.error verbose

%parse-param { id <ParserConsumer> consumer }

%union {
    char *stringValue;
    int numericValue;
}

%token <stringValue> Token_String
%token <numericValue> Token_Number

%%

/* http://www.tldp.org/HOWTO/Lex-YACC-HOWTO-6.html 6.2 Recursion: 'right is wrong' */
tokens: /* empty */
      | tokens token

token:
    Token_String {

        std::cout << "Parser says: Hello from C++\n";

        printf("[Parser, string] %s\n", $1);

        [consumer parserDidParseString:$1];

        free($1);
    }
    | Token_Number {
        printf("[Parser, number]\n");

        [consumer parserDidParseNumber:$1];
    }
%%

Makefile:

generate-parser: clean flex bison

clean:
    rm -rf './Parser/Generated Code'
    mkdir -p './Parser/Generated Code'

flex:
    # brew install flex
    /usr/local/bin/flex ./Parser/Lexer.lm

bison:
    # brew install bison
    /usr/local/bin/bison -d ./Parser/Parser.ym