There's a quick and easy way around the 'static' issue. I was trying to print a human readable abstract syntax tree with string representations of each non-terminal for my C to 6502 compiler. Here's what I did...
In your .y file in the last section, create a non-static variable called token_table
%%
#include <stdio.h>
extern char yytext[];
extern int column;
const char ** token_table;
...
Now, in the main method that calls yyparse, assign yytname to token_table
int main(int argc, char ** argv) {
FILE * myfile;
yydebug = 1;
token_table = yytname;
...
Now, you can access token_table in any compilation unit simply by declaring it as an extern, as in:
extern const char ** token_table;
/* Using it later in that same compilation unit */
printf("%s", token_table[DOWHILE - 258 + 3]); /* prints "DOWHILE" */
For each node in your AST, if you assign it the yytokentype value found in y.tab.h, you simply subtract 258 and add 3 to index into token_table (yytname). You have to subtract 258 b/c that is where yytokentype starts enumerating at and you have to add 3 b/c yytname adds the three reserved symbols ("$end", "error", and "$undefined") at the start of the table.
For instance, my generated bison file has:
static const char *const yytname[] =
{
"$end", "error", "$undefined", "DOWHILE", "UAND", "UMULT", "UPLUS",
"UMINUS", "UBANG", "UTILDE", "ARR", "NOOP", "MEMBER", "POSTINC",
...
And, the defines header (run bison with the --defines=y.tab.h option):
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
DOWHILE = 258,
UAND = 259,
UMULT = 260,
...