[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Bison 1.50 experiences

From: Hans Aberg
Subject: Bison 1.50 experiences
Date: Sun, 13 Oct 2002 19:42:43 +0200

I tried to compile Bison 1.50 with my old MacOS pre-X Metrowerks compiler.
Some notes:

- I had a problem with the library header bitset.h because there is a C++
header <bitset>, and on my system there was also a <bitset.h> compatibility
header. There is though no requirement in the C++ standard of a <bitset.h>

- Because there are no pipes under the MacOS pre-X that I use, I integrated
M4 with Bison. One then has to watch out that some GNU libraries relies on
global variables that must be initialized, but which the program may not do
explicitly. For example, optarg is such a library, which uses optind which
must then be set to 0 before m4 is invoked.

Also, M4 does not seem at all designed for such an integration, as is
terminates  using exit(). Therefore, it seems safest to not integrate M4
with Bison directly on the source code level.

Here is how I merged M4 and Bison:
 - I changed the main of m4 to:
  extern int optind;
  int m4_main (int argc, char *const *argv, const char* outname, FILE**
outfile) {...
so that its name does not clash with the main of Bison.

 - The extra arguments "const char* outname" and "FILE** outfile" are used
to redirect the stdout of M4 to a temporary file (at the beginning of
  *outfile = freopen(outname, "w+", stdout);
  optind = 0; /* Must be set to 0 before use -- already used by Bison */
This must be a named temporary file it seems, because there is no standard
command reopen to an already open stream, it seems.

 - The next problem is that m4 exits by abort() and exit(). If Bison should
work as a DLL, these must somehow be picked up. So I added a prefix before
the m4 sources are read:
  void Exit(int status);
  void abort_(void);
  #define exit Exit
  #define abort abort_
Then Exit() and abort_() will be called instead of the usual exit() and
abort() (the funny names were chosen to avoid clashes on my system).

The idea is to exploit this to via atexit() install a suitable longjump:

File scan-skel.c:
  int m4_main (int argc, char *const *argv, const char* outname, FILE**

  int m4_argc;
  char* m4_argv[10];

  void Exit(int status) {
    exit_status = status;
    /* Make sure exit() calls exit() and not Exit() here: */
    #undef exit

  void abort_(void) {

  #include <setjmp.h>
  jmp_buf bison_env;
  int exit_status;
  void m4_exit_jump(void) {
    longjmp(bison_env, 1);

m4_invoke (const char *definitions) {
  tempout = stringappend(short_base_name, ".m4.out");
  m4_argc = 4;
  m4_argv[0] = "m4";
  m4_argv[1] = "m4sugar.m4";
  m4_argv[2] = (char*)definitions;
  m4_argv[3] = (char*)skeleton;
  if (setjmp(bison_env) == 0)
    m4_main(m4_argc, m4_argv, tempout, &skel_in);
  if (exit_status == EXIT_FAILURE)
  XFREE (full_skeleton);
  if (!skel_in)
    error (EXIT_FAILURE, errno, "cannot run m4");
  rewind(skel_in);  /* rewind */
  skel_lex ();
  /* Reclaim Flex's buffers.  */
  yy_delete_buffer (YY_CURRENT_BUFFER);
  fclose(skel_in);  /* close file after use */

 - I compiled m4 as a library, removing all its library files, except the
regex ones.

- I think that one should try to strive for a Bison which does not rely on
temporary files, which tend to litter the directories after use (especially
if the program terminates abnormally or is used as a DLL).

One way to avoid temporary files littering the directory is to make the
names of them explicit and then always use the same name according to some
scheme. For example, when compiling the file <name>.y, one may produce an
output file say <name>.y.m4, which in its turn is handed over to m4. The
ideal would be if a program like m4 could produce the output file directly
without additional post processing by Bison (I could not find any such
command in m4, though).

I used:
  stringappend(short_base_name, ".m4");
  stringappend(short_base_name, ".m4.out");
for the file handed over to M4 and the file that M4 outputs.

Also note that there are Std C facilities for creating temporary files:
  FILE* tmpfile(void);
  char* tmpnam(char*):
The first one creates a temporary unnamed file that is deleted when the
program terminates (which it doesn't when used as a DLL) or when the file
is closed.

- File scan-skel.l forgets to close yyskel before a new one is opened:
  /* This is an approximation, but we don't need more. */
^"#output \""[^\"]+\"\n        {
     yytext[yyleng - 2] = '\0';
     XFREE (yyoutname);
     yyoutname = xstrdup (yytext + strlen ("#output \""));
     if (yyout != stdout)
     yyout = xfopen (yyoutname, "w");
     yylineno = 1;
This becomes a problem if Bison is made into library, like DLL used with an
IDE (Integrated Development Environment).

Also not that if one follows the instructions above, stdout has been reset
to a file that the parser is reading, so it should not be closed.

  Hans Aberg

reply via email to

[Prev in Thread] Current Thread [Next in Thread]