Search code examples
cgnuflex-lexer

How to redirect yyout to char* buffer in GNU flex?


By default yyout is set to stdout and can be redirected to FILE* stream. Is a way to redirect yyout to char*?


Solution

  • There are a number of ways to do it. If you post a bit of your scanner code where you are trying to use yyout may be I can give you a more specific answer:

    Typically it's in your action where you'd do this. So instead of using ECHO or fprintf(yyout... you'd use something else like

    <token>  { snprintf(buf, sizeof(buf), "%s", yytext); }
    

    earlier you'd have declared:

    char buf[BUFSIZE];
    

    addendum I - An alternate approach

    There are clever ways of dealing with the situation, but they are prone to maintenance problems in the long run because they are "clever" .. .and the cleverer a solution the shorter its life. (unless the cleverness is well documented with caveats attached)

     int yyprintf(const char *fmt, ...)  {
         va_list ap;
         va_start(ap, fmt);
         if ( some_flag & FLAG_OUTFILE ) {
             vfprintf(yyout, fmt, ap);
         }
         else {
             sprintf(buf, fmt, ap);
         }
         va_end(ap);
     }
    

    where buf is a global buffer.

    However if you want to make things a bit local:

    Approach 2: Fine-grain control over where things go and when

    You want fine grain control over where things go along the way. Sometimes you want output to file, other times to a string, and you don't always know which is which and when and where you can use something like this:

     int myvprintf(void *here, size_t len, const char *fmt, va_list ap) 
          __attribute__((format (gnu_printf, 3, 4), nonnull(1, 3))) {
         
         int rv;
    
         if ( len > 0 ) { 
            rv = vsnprintf((char *), len, fmt, ap);
         }
         else {
            rv = vfprintf((FILE *)here, fmt, ap);
         }
    
         return rv;
     }
    
     int myprintf(void *here, size_t len, const char *fmt, ... ) 
          __attribute__((format (gnu_printf, 3, 4), nonnull(1, 3))) {
         int rv;
         va_list ap;
         va_start(ap, fmt);
         rv = myvprintf(here, len, fmt, ap);
         va_end(ap);
         return rv;
     }
    

    and use myprintf along the way, you will have control over what is here all the time.

    Just for Fun

    Don't try this at home. But all yy* identifiers and ECHO are not plain variables, they are #define's. So you could do some clever macro rewriting:

    For example, if you are using ECHO everywhere then you can redefine it to do whatever you like (just #undef and #define) in the top section:

    %{
    #undef ECHO
    #define ECHO snprintf(buf, sizeof(buf), "%s", yytext)
    %}
    
    %%
    
    <token> ECHO;
    %% 
    

    just hide it all in convoluted headers and do other cleverness that will make debugging a hell later for a programmer you hate. This can have its own rewards and giggles.