a68g.c

     
   1  //! @file a68g.c
   2  //! @author J. Marcel van der Veer
   3  //!
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2023 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  //!
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! Algol 68 Genie main driver.
  25  
  26  #include "a68g.h"
  27  #include "a68g-genie.h"
  28  #include "a68g-listing.h"
  29  #include "a68g-mp.h"
  30  #include "a68g-optimiser.h"
  31  #include "a68g-options.h"
  32  #include "a68g-parser.h"
  33  #include "a68g-postulates.h"
  34  #include "a68g-prelude.h"
  35  #include "a68g-prelude-mathlib.h"
  36  #include "a68g-quad.h"
  37  
  38  #if defined (HAVE_MATHLIB)
  39  #include <Rmath.h>
  40  #endif
  41  
  42  GLOBALS_T common;
  43  
  44  #define EXTENSIONS 11
  45  static char *extensions[EXTENSIONS] = {
  46    NO_TEXT,
  47    ".a68", ".A68",
  48    ".a68g", ".A68G",
  49    ".algol", ".ALGOL",
  50    ".algol68", ".ALGOL68",
  51    ".algol68g", ".ALGOL68G"
  52  };
  53  
  54  void compiler_interpreter (void);
  55  
  56  //! @brief Verbose statistics, only useful when debugging a68g.
  57  
  58  void verbosity (void)
  59  {
  60  #if defined (A68_DEBUG)
  61    ;
  62  #else
  63    ;
  64  #endif
  65  }
  66  
  67  //! @brief State license of running a68g image.
  68  
  69  void state_license (FILE_T f)
  70  {
  71  #define PR(s)\
  72    ASSERT (snprintf(A68 (output_line), SNPRINTF_SIZE, "%s\n", (s)) >= 0);\
  73    WRITE (f, A68 (output_line));
  74  //
  75    if (f == STDOUT_FILENO) {
  76      io_close_tty_line ();
  77    }
  78    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "Algol 68 Genie %s\n", PACKAGE_VERSION) >= 0);
  79    WRITE (f, A68 (output_line));
  80    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "Copyright 2001-2023 %s.\n", PACKAGE_BUGREPORT) >= 0);
  81    WRITE (f, A68 (output_line));
  82    PR ("");
  83    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "This is free software covered by the GNU General Public License.\n") >= 0);
  84    WRITE (f, A68 (output_line));
  85    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "There is ABSOLUTELY NO WARRANTY for Algol 68 Genie;\n") >= 0);
  86    WRITE (f, A68 (output_line));
  87    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n") >= 0);
  88    WRITE (f, A68 (output_line));
  89    PR ("See the GNU General Public License for more details.");
  90    PR ("");
  91    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "Please report bugs to %s.\n", PACKAGE_BUGREPORT) >= 0);
  92    WRITE (f, A68 (output_line));
  93  #undef PR
  94  }
  95  
  96  //! @brief State version of running a68g image.
  97  
  98  void state_version (FILE_T f)
  99  {
 100  #define PR(s)\
 101    ASSERT (snprintf(A68 (output_line), SNPRINTF_SIZE, "%s\n", (s)) >= 0);\
 102    WRITE (f, A68 (output_line));
 103  //
 104    if (f == STDOUT_FILENO) {
 105      io_close_tty_line ();
 106    }
 107    state_license (f);
 108    PR ("");
 109  #if defined (BUILD_WIN32)
 110    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "WIN32 executable\n") >= 0);
 111    WRITE (f, A68 (output_line));
 112    WRITELN (f, "");
 113  #endif
 114  #if (A68_LEVEL >= 3)
 115    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With hardware support for long modes\n") >= 0);
 116    WRITE (f, A68 (output_line));
 117  #endif
 118  #if defined (BUILD_A68_COMPILER) && defined (C_COMPILER)
 119    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With plugin-compilation support (back-end is %s)\n", C_COMPILER) >= 0);
 120    WRITE (f, A68 (output_line));
 121  #elif defined (BUILD_A68_COMPILER)
 122    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With plugin-compilation support\n") >= 0);
 123    WRITE (f, A68 (output_line));
 124  #endif
 125  #if defined (BUILD_PARALLEL_CLAUSE)
 126    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With parallel-clause support\n") >= 0);
 127    WRITE (f, A68 (output_line));
 128  #endif
 129  #if defined (HAVE_GNU_MPFR)
 130    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With GNU MP %s\n", gmp_version) >= 0);
 131    WRITE (f, A68 (output_line));
 132    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With GNU MPFR %s\n", mpfr_get_version ()) >= 0);
 133    WRITE (f, A68 (output_line));
 134  #endif
 135  #if defined (HAVE_MATHLIB)
 136    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With mathlib from R %s\n", R_VERSION_STRING) >= 0);
 137    WRITE (f, A68 (output_line));
 138  #endif
 139  #if defined (HAVE_GSL)
 140    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With GNU Scientific Library %s\n", GSL_VERSION) >= 0);
 141    WRITE (f, A68 (output_line));
 142  #endif
 143  #if defined (HAVE_GNU_PLOTUTILS)
 144    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With GNU plotutils %s\n", PL_LIBPLOT_VER_STRING) >= 0);
 145    WRITE (f, A68 (output_line));
 146  #endif
 147  #if defined (HAVE_CURSES)
 148    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With curses %s\n", NCURSES_VERSION) >= 0);
 149    WRITE (f, A68 (output_line));
 150  #endif
 151  #if defined (BUILD_HTTP)
 152    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With TCP/IP support\n") >= 0);
 153    WRITE (f, A68 (output_line));
 154  #endif
 155  #if defined (HAVE_POSTGRESQL)
 156    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "With PostgreSQL support\n") >= 0);
 157    WRITE (f, A68 (output_line));
 158  #endif
 159  #if defined (_CS_GNU_LIBC_VERSION) && defined (BUILD_UNIX)
 160    if (confstr (_CS_GNU_LIBC_VERSION, A68 (input_line), BUFFER_SIZE) > (size_t) 0) {
 161      ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "GNU libc version %s\n", A68 (input_line)) >= 0);
 162      WRITE (f, A68 (output_line));
 163    }
 164  #if (defined (BUILD_PARALLEL_CLAUSE) && defined (_CS_GNU_LIBPTHREAD_VERSION))
 165    if (confstr (_CS_GNU_LIBPTHREAD_VERSION, A68 (input_line), BUFFER_SIZE) > (size_t) 0) {
 166      ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "GNU libpthread version %s\n", A68 (input_line)) >= 0);
 167      WRITE (f, A68 (output_line));
 168    }
 169  #endif
 170  #endif
 171  #if defined (HPA_VERSION)
 172    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "HPA version %s\n", HPA_VERSION) >= 0);
 173    WRITE (f, A68 (output_line));
 174  #endif
 175  ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "Build %d.%d.%d.%d.%d %s\n", A68_LEVEL, (int) sizeof (INT_T), (int) sizeof (REAL_T), (int) sizeof (MP_INT_T), (int) sizeof (MP_REAL_T), __DATE__) >= 0);
 176  WRITE (f, A68 (output_line));
 177  #undef PR
 178  }
 179  
 180  //! @brief Give brief help if someone types 'a68g --help'.
 181  
 182  void online_help (FILE_T f)
 183  {
 184    if (f == STDOUT_FILENO) {
 185      io_close_tty_line ();
 186    }
 187    state_license (f);
 188    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "Usage: %s [options | filename]", A68 (a68_cmd_name)) >= 0);
 189    WRITELN (f, A68 (output_line));
 190    ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "For help: %s --apropos [keyword]", A68 (a68_cmd_name)) >= 0);
 191    WRITELN (f, A68 (output_line));
 192  }
 193  
 194  //! @brief Start book keeping for a phase.
 195  
 196  void announce_phase (char *t)
 197  {
 198    if (OPTION_VERBOSE (&A68_JOB)) {
 199      ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "%s: %s", A68 (a68_cmd_name), t) >= 0);
 200      io_close_tty_line ();
 201      WRITE (STDOUT_FILENO, A68 (output_line));
 202    }
 203  }
 204  
 205  //! @brief Test extension and strip.
 206  
 207  BOOL_T strip_extension (char *ext)
 208  {
 209    if (ext == NO_TEXT) {
 210      return A68_FALSE;
 211    }
 212    int nlen = (int) strlen (FILE_SOURCE_NAME (&A68_JOB)), xlen = (int) strlen (ext);
 213    if (nlen > xlen && strcmp (&(FILE_SOURCE_NAME (&A68_JOB)[nlen - xlen]), ext) == 0) {
 214      char *fn = (char *) get_heap_space ((size_t) (nlen + 1));
 215      bufcpy (fn, FILE_SOURCE_NAME (&A68_JOB), nlen);
 216      fn[nlen - xlen] = NULL_CHAR;
 217      a68_free (FILE_GENERIC_NAME (&A68_JOB));
 218      FILE_GENERIC_NAME (&A68_JOB) = new_string (fn, NO_TEXT);
 219      a68_free (fn);
 220      return A68_TRUE;
 221    } else {
 222      return A68_FALSE;
 223    }
 224  }
 225  
 226  //! @brief Try opening with an extension.
 227  
 228  void open_with_extensions (void)
 229  {
 230    FILE_SOURCE_FD (&A68_JOB) = -1;
 231    for (int k = 0; k < EXTENSIONS && FILE_SOURCE_FD (&A68_JOB) == -1; k++) {
 232      int len;
 233      char *fn = NULL;
 234      if (extensions[k] == NO_TEXT) {
 235        len = (int) strlen (FILE_INITIAL_NAME (&A68_JOB)) + 1;
 236        fn = (char *) get_heap_space ((size_t) len);
 237        bufcpy (fn, FILE_INITIAL_NAME (&A68_JOB), len);
 238      } else {
 239        len = (int) strlen (FILE_INITIAL_NAME (&A68_JOB)) + (int) strlen (extensions[k]) + 1;
 240        fn = (char *) get_heap_space ((size_t) len);
 241        bufcpy (fn, FILE_INITIAL_NAME (&A68_JOB), len);
 242        bufcat (fn, extensions[k], len);
 243      }
 244      FILE_SOURCE_FD (&A68_JOB) = open (fn, O_RDONLY | O_BINARY);
 245      if (FILE_SOURCE_FD (&A68_JOB) != -1) {
 246        BOOL_T cont = A68_TRUE;
 247        a68_free (FILE_SOURCE_NAME (&A68_JOB));
 248        a68_free (FILE_GENERIC_NAME (&A68_JOB));
 249        FILE_SOURCE_NAME (&A68_JOB) = new_string (fn, NO_TEXT);
 250        FILE_GENERIC_NAME (&A68_JOB) = new_string (a68_basename (fn), NO_TEXT);
 251        FILE_PATH (&A68_JOB) = new_string (a68_dirname (fn), NO_TEXT);
 252        for (int l = 0; l < EXTENSIONS && cont; l++) {
 253          if (strip_extension (extensions[l])) {
 254            cont = A68_FALSE;
 255          }
 256        }
 257      }
 258      a68_free (fn);
 259    }
 260  }
 261  
 262  //! @brief Remove a regular file.
 263  
 264  void a68_rm (char *fn)
 265  {
 266    struct stat path_stat;
 267    if (stat (fn, &path_stat) == 0) {
 268      if (S_ISREG (path_stat.st_mode)) {
 269        ABEND (remove (fn) != 0, ERROR_ACTION, FILE_OBJECT_NAME (&A68_JOB));
 270      }
 271    }
 272  }
 273  
 274  //! @brief Drives compilation and interpretation.
 275  
 276  void compiler_interpreter (void)
 277  {
 278    int len, num;
 279  #if defined (BUILD_A68_COMPILER)
 280    BOOL_T emitted = A68_FALSE;
 281  #endif
 282    TREE_LISTING_SAFE (&A68_JOB) = A68_FALSE;
 283    CROSS_REFERENCE_SAFE (&A68_JOB) = A68_FALSE;
 284    A68 (in_execution) = A68_FALSE;
 285    A68 (new_nodes) = 0;
 286    A68 (new_modes) = 0;
 287    A68 (new_postulates) = 0;
 288    A68 (new_node_infos) = 0;
 289    A68 (new_genie_infos) = 0;
 290    A68 (symbol_table_count) = 0;
 291    A68 (mode_count) = 0;
 292    A68 (node_register) = NO_VAR;
 293    init_postulates ();
 294    A68 (do_confirm_exit) = A68_TRUE;
 295    A68 (f_entry) = NO_NODE;
 296    A68 (global_level) = 0;
 297    A68 (max_lex_lvl) = 0;
 298    A68_PARSER (stop_scanner) = A68_FALSE;
 299    A68_PARSER (read_error) = A68_FALSE;
 300    A68_PARSER (no_preprocessing) = A68_FALSE;
 301    A68_PARSER (reductions) = 0;
 302    A68_PARSER (tag_number) = 0;
 303    A68 (curses_mode) = A68_FALSE;
 304    A68 (top_soid_list) = NO_SOID;
 305    A68 (max_simplout_size) = 0;
 306    A68_MON (in_monitor) = A68_FALSE;
 307    A68_MP (mp_ln_scale_size) = -1;
 308    A68_MP (mp_ln_10_size) = -1;
 309    A68_MP (mp_gamma_size) = -1;
 310    A68_MP (mp_one_size) = -1;
 311    A68_MP (mp_pi_size) = -1;
 312  // File set-up.
 313    SCAN_ERROR (FILE_INITIAL_NAME (&A68_JOB) == NO_TEXT, NO_LINE, NO_TEXT, ERROR_NO_SOURCE_FILE);
 314    FILE_BINARY_OPENED (&A68_JOB) = A68_FALSE;
 315    FILE_BINARY_WRITEMOOD (&A68_JOB) = A68_TRUE;
 316    FILE_PLUGIN_OPENED (&A68_JOB) = A68_FALSE;
 317    FILE_PLUGIN_WRITEMOOD (&A68_JOB) = A68_TRUE;
 318    FILE_LISTING_OPENED (&A68_JOB) = A68_FALSE;
 319    FILE_LISTING_WRITEMOOD (&A68_JOB) = A68_TRUE;
 320    FILE_OBJECT_OPENED (&A68_JOB) = A68_FALSE;
 321    FILE_OBJECT_WRITEMOOD (&A68_JOB) = A68_TRUE;
 322    FILE_PRETTY_OPENED (&A68_JOB) = A68_FALSE;
 323    FILE_SCRIPT_OPENED (&A68_JOB) = A68_FALSE;
 324    FILE_SCRIPT_WRITEMOOD (&A68_JOB) = A68_FALSE;
 325    FILE_SOURCE_OPENED (&A68_JOB) = A68_FALSE;
 326    FILE_SOURCE_WRITEMOOD (&A68_JOB) = A68_FALSE;
 327    FILE_DIAGS_OPENED (&A68_JOB) = A68_FALSE;
 328    FILE_DIAGS_WRITEMOOD (&A68_JOB) = A68_TRUE;
 329  // Open the source file. 
 330  // Open it for binary reading for systems that require so (Win32).
 331  // Accept various silent extensions.
 332    errno = 0;
 333    FILE_SOURCE_NAME (&A68_JOB) = NO_TEXT;
 334    FILE_GENERIC_NAME (&A68_JOB) = NO_TEXT;
 335    open_with_extensions ();
 336    if (FILE_SOURCE_NAME (&A68_JOB) == NO_TEXT) {
 337      errno = ENOENT;
 338      SCAN_ERROR (A68_TRUE, NO_LINE, NO_TEXT, ERROR_SOURCE_FILE_OPEN);
 339    } else {
 340      struct stat path_stat;
 341      errno = 0;
 342      SCAN_ERROR (stat (FILE_SOURCE_NAME (&A68_JOB), &path_stat) != 0, NO_LINE, NO_TEXT, ERROR_SOURCE_FILE_OPEN);
 343      SCAN_ERROR (S_ISDIR (path_stat.st_mode), NO_LINE, NO_TEXT, ERROR_IS_DIRECTORY);
 344      SCAN_ERROR (!S_ISREG (path_stat.st_mode), NO_LINE, NO_TEXT, ERROR_NO_REGULAR_FILE);
 345    }
 346    if (FILE_SOURCE_FD (&A68_JOB) == -1) {
 347      scan_error (NO_LINE, NO_TEXT, ERROR_SOURCE_FILE_OPEN);
 348    }
 349    ABEND (FILE_SOURCE_NAME (&A68_JOB) == NO_TEXT, ERROR_INTERNAL_CONSISTENCY, __func__);
 350    ABEND (FILE_GENERIC_NAME (&A68_JOB) == NO_TEXT, ERROR_INTERNAL_CONSISTENCY, __func__);
 351  // Object file.
 352    len = 1 + (int) strlen (FILE_GENERIC_NAME (&A68_JOB)) + (int) strlen (OBJECT_EXTENSION);
 353    FILE_OBJECT_NAME (&A68_JOB) = (char *) get_heap_space ((size_t) len);
 354    bufcpy (FILE_OBJECT_NAME (&A68_JOB), FILE_GENERIC_NAME (&A68_JOB), len);
 355    bufcat (FILE_OBJECT_NAME (&A68_JOB), OBJECT_EXTENSION, len);
 356  // Binary.
 357    len = 1 + (int) strlen (FILE_GENERIC_NAME (&A68_JOB)) + (int) strlen (PLUGIN_EXTENSION);
 358    FILE_BINARY_NAME (&A68_JOB) = (char *) get_heap_space ((size_t) len);
 359    bufcpy (FILE_BINARY_NAME (&A68_JOB), FILE_GENERIC_NAME (&A68_JOB), len);
 360    bufcat (FILE_BINARY_NAME (&A68_JOB), BINARY_EXTENSION, len);
 361  // Library file.
 362    len = 1 + (int) strlen (FILE_GENERIC_NAME (&A68_JOB)) + (int) strlen (PLUGIN_EXTENSION);
 363    FILE_PLUGIN_NAME (&A68_JOB) = (char *) get_heap_space ((size_t) len);
 364    bufcpy (FILE_PLUGIN_NAME (&A68_JOB), FILE_GENERIC_NAME (&A68_JOB), len);
 365    bufcat (FILE_PLUGIN_NAME (&A68_JOB), PLUGIN_EXTENSION, len);
 366  // Listing file.
 367    len = 1 + (int) strlen (FILE_GENERIC_NAME (&A68_JOB)) + (int) strlen (LISTING_EXTENSION);
 368    FILE_LISTING_NAME (&A68_JOB) = (char *) get_heap_space ((size_t) len);
 369    bufcpy (FILE_LISTING_NAME (&A68_JOB), FILE_GENERIC_NAME (&A68_JOB), len);
 370    bufcat (FILE_LISTING_NAME (&A68_JOB), LISTING_EXTENSION, len);
 371  // Pretty file.
 372    len = 1 + (int) strlen (FILE_GENERIC_NAME (&A68_JOB)) + (int) strlen (PRETTY_EXTENSION);
 373    FILE_PRETTY_NAME (&A68_JOB) = (char *) get_heap_space ((size_t) len);
 374    bufcpy (FILE_PRETTY_NAME (&A68_JOB), FILE_GENERIC_NAME (&A68_JOB), len);
 375    bufcat (FILE_PRETTY_NAME (&A68_JOB), PRETTY_EXTENSION, len);
 376  // Script file.
 377    len = 1 + (int) strlen (FILE_GENERIC_NAME (&A68_JOB)) + (int) strlen (SCRIPT_EXTENSION);
 378    FILE_SCRIPT_NAME (&A68_JOB) = (char *) get_heap_space ((size_t) len);
 379    bufcpy (FILE_SCRIPT_NAME (&A68_JOB), FILE_GENERIC_NAME (&A68_JOB), len);
 380    bufcat (FILE_SCRIPT_NAME (&A68_JOB), SCRIPT_EXTENSION, len);
 381  // Parser.
 382    a68_parser ();
 383    if (TOP_NODE (&A68_JOB) == NO_NODE) {
 384      errno = ECANCELED;
 385      ABEND (A68_TRUE, ERROR_SOURCE_FILE_EMPTY, NO_TEXT);
 386    }
 387  // Portability checker.
 388    if (ERROR_COUNT (&A68_JOB) == 0) {
 389      announce_phase ("portability checker");
 390      portcheck (TOP_NODE (&A68_JOB));
 391      verbosity ();
 392    }
 393  // Finalise syntax tree.
 394    if (ERROR_COUNT (&A68_JOB) == 0) {
 395      num = 0;
 396      renumber_nodes (TOP_NODE (&A68_JOB), &num);
 397      NEST (TABLE (TOP_NODE (&A68_JOB))) = A68 (symbol_table_count) = 3;
 398      reset_symbol_table_nest_count (TOP_NODE (&A68_JOB));
 399      verbosity ();
 400    }
 401  //
 402    if (A68_MP (varying_mp_digits) > width_to_mp_digits (MP_MAX_DECIMALS)) {
 403      diagnostic (A68_WARNING, NO_NODE, WARNING_PRECISION, NO_LINE, 0, A68_MP (varying_mp_digits) * LOG_MP_RADIX);
 404    }
 405  // Compiler.
 406    if (ERROR_COUNT (&A68_JOB) == 0 && OPTION_OPT_LEVEL (&A68_JOB) > NO_OPTIMISE) {
 407      announce_phase ("optimiser (code generator)");
 408      num = 0;
 409      renumber_nodes (TOP_NODE (&A68_JOB), &num);
 410      A68 (node_register) = (NODE_T **) get_heap_space ((size_t) num * sizeof (NODE_T));
 411      ABEND (A68 (node_register) == NO_VAR, ERROR_ACTION, __func__);
 412      register_nodes (TOP_NODE (&A68_JOB));
 413      FILE_OBJECT_FD (&A68_JOB) = open (FILE_OBJECT_NAME (&A68_JOB), O_WRONLY | O_CREAT | O_TRUNC, A68_PROTECTION);
 414      ABEND (FILE_OBJECT_FD (&A68_JOB) == -1, ERROR_ACTION, FILE_OBJECT_NAME (&A68_JOB));
 415      FILE_OBJECT_OPENED (&A68_JOB) = A68_TRUE;
 416      compiler (FILE_OBJECT_FD (&A68_JOB));
 417      ASSERT (close (FILE_OBJECT_FD (&A68_JOB)) == 0);
 418      FILE_OBJECT_OPENED (&A68_JOB) = A68_FALSE;
 419  #if defined (BUILD_A68_COMPILER)
 420      emitted = A68_TRUE;
 421  #endif
 422    }
 423  #if defined (BUILD_A68_COMPILER)
 424  // Only compile C if the A68 compiler found no errors (constant folder for instance).
 425    if (ERROR_COUNT (&A68_JOB) == 0 && OPTION_OPT_LEVEL (&A68_JOB) > 0 && !OPTION_RUN_SCRIPT (&A68_JOB)) {
 426      BUFFER cmd, options;
 427      if (OPTION_RERUN (&A68_JOB) == A68_FALSE) {
 428        announce_phase ("optimiser (code compiler)");
 429        errno = 0;
 430  //
 431  // Compilation on Linux, BSD.
 432  // Build shared library using gcc or clang.
 433  // TODO: One day this should be all portable between platforms.
 434  //
 435  // -fno-stack-protector is needed for OS's that enforce -fstack-protector-strong which may give
 436  // undefined reference to `__stack_chk_fail_local'
 437  // by ld. Ubuntu is one such.
 438  //
 439        ASSERT (snprintf (options, SNPRINTF_SIZE, "%s %s", optimisation_option (), A68_GCC_OPTIONS) >= 0);
 440  #if defined (HAVE_PIC)
 441        bufcat (options, " ", BUFFER_SIZE);
 442        bufcat (options, HAVE_PIC, BUFFER_SIZE);
 443  #endif
 444        ASSERT (snprintf (cmd, SNPRINTF_SIZE, "%s -I%s %s -c -o \"%s\" \"%s\"", C_COMPILER, INCLUDEDIR, options, FILE_BINARY_NAME (&A68_JOB), FILE_OBJECT_NAME (&A68_JOB)) >= 0);
 445        ABEND (system (cmd) != 0, ERROR_ACTION, cmd);
 446        ASSERT (snprintf (cmd, SNPRINTF_SIZE, "ld -export-dynamic -shared -o \"%s\" \"%s\"", FILE_PLUGIN_NAME (&A68_JOB), FILE_BINARY_NAME (&A68_JOB)) >= 0);
 447        ABEND (system (cmd) != 0, ERROR_ACTION, cmd);
 448        a68_rm (FILE_BINARY_NAME (&A68_JOB));
 449      }
 450      verbosity ();
 451    }
 452  #else
 453    if (OPTION_OPT_LEVEL (&A68_JOB) > 0) {
 454      diagnostic (A68_WARNING | A68_FORCE_DIAGNOSTICS, TOP_NODE (&A68_JOB), WARNING_OPTIMISATION);
 455    }
 456  #endif
 457  // Indenter.
 458    if (ERROR_COUNT (&A68_JOB) == 0 && OPTION_PRETTY (&A68_JOB)) {
 459      announce_phase ("indenter");
 460      indenter (&A68_JOB);
 461      verbosity ();
 462    }
 463  // Interpreter.
 464    diagnostics_to_terminal (TOP_LINE (&A68_JOB), A68_ALL_DIAGNOSTICS);
 465  // Restore seed for rng.
 466    GetRNGstate ();
 467    A68 (f_entry) = TOP_NODE (&A68_JOB);
 468  //
 469    if (ERROR_COUNT (&A68_JOB) == 0 && OPTION_COMPILE (&A68_JOB) == A68_FALSE && (OPTION_CHECK_ONLY (&A68_JOB) ? OPTION_RUN (&A68_JOB) : A68_TRUE)) {
 470  #if defined (BUILD_A68_COMPILER)
 471      void *compile_plugin;
 472  #endif
 473      A68 (close_tty_on_exit) = A68_FALSE;        // Assuming no runtime errors a priori
 474  #if defined (BUILD_A68_COMPILER)
 475      if (OPTION_RUN_SCRIPT (&A68_JOB)) {
 476        rewrite_script_source ();
 477      }
 478  #endif
 479      if (OPTION_DEBUG (&A68_JOB)) {
 480        state_license (STDOUT_FILENO);
 481      }
 482  #if defined (BUILD_A68_COMPILER)
 483      if (OPTION_OPT_LEVEL (&A68_JOB) > 0) {
 484        char plugin_name[BUFFER_SIZE];
 485        void *a68_plugin;
 486        struct stat srcstat, objstat;
 487        int ret;
 488        announce_phase ("dynamic linker");
 489        ASSERT (snprintf (plugin_name, SNPRINTF_SIZE, "%s", FILE_PLUGIN_NAME (&A68_JOB)) >= 0);
 490  // Correction when pwd is outside LD_PLUGIN_PATH.
 491  // The DL cannot be loaded if it is.
 492        if (strcmp (plugin_name, a68_basename (plugin_name)) == 0) {
 493          ASSERT (snprintf (plugin_name, SNPRINTF_SIZE, "./%s", FILE_PLUGIN_NAME (&A68_JOB)) >= 0);
 494        }
 495  // Check whether we are doing something rash.
 496        ret = stat (FILE_SOURCE_NAME (&A68_JOB), &srcstat);
 497        ABEND (ret != 0, ERROR_ACTION, FILE_SOURCE_NAME (&A68_JOB));
 498        ret = stat (plugin_name, &objstat);
 499        ABEND (ret != 0, ERROR_ACTION, plugin_name);
 500        if (OPTION_RERUN (&A68_JOB)) {
 501          ABEND (ST_MTIME (&srcstat) > ST_MTIME (&objstat), "plugin outdates source", "cannot RERUN");
 502        }
 503  // First load a68g itself so compiler code can resolve a68g symbols.
 504        a68_plugin = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
 505        ABEND (a68_plugin == NULL, ERROR_CANNOT_OPEN_PLUGIN, dlerror ());
 506  // Then load compiler code.
 507        compile_plugin = dlopen (plugin_name, RTLD_NOW | RTLD_GLOBAL);
 508        ABEND (compile_plugin == NULL, ERROR_CANNOT_OPEN_PLUGIN, dlerror ());
 509      } else {
 510        compile_plugin = NULL;
 511      }
 512      announce_phase ("genie");
 513      genie (compile_plugin);
 514  // Unload compiler plugin.
 515      if (OPTION_OPT_LEVEL (&A68_JOB) > 0) {
 516        int ret = dlclose (compile_plugin);
 517        ABEND (ret != 0, ERROR_ACTION, dlerror ());
 518      }
 519  #else
 520      announce_phase ("genie");
 521      genie (NO_NODE);
 522  #endif
 523  // Free heap allocated by genie.
 524      genie_free (TOP_NODE (&A68_JOB));
 525  // Store seed for rng.
 526      announce_phase ("store rng state");
 527      PutRNGstate ();
 528  // Normal end of program.
 529      diagnostics_to_terminal (TOP_LINE (&A68_JOB), A68_RUNTIME_ERROR);
 530      if (OPTION_DEBUG (&A68_JOB) || OPTION_TRACE (&A68_JOB) || OPTION_CLOCK (&A68_JOB)) {
 531        ASSERT (snprintf (A68 (output_line), SNPRINTF_SIZE, "\nGenie finished in %.2f seconds\n", seconds () - A68 (cputime_0)) >= 0);
 532        WRITE (STDOUT_FILENO, A68 (output_line));
 533      }
 534      verbosity ();
 535    }
 536  // Setting up listing file.
 537    announce_phase ("write listing");
 538    if (OPTION_MOID_LISTING (&A68_JOB) || OPTION_TREE_LISTING (&A68_JOB) || OPTION_SOURCE_LISTING (&A68_JOB) || OPTION_OBJECT_LISTING (&A68_JOB) || OPTION_STATISTICS_LISTING (&A68_JOB)) {
 539      FILE_LISTING_FD (&A68_JOB) = open (FILE_LISTING_NAME (&A68_JOB), O_WRONLY | O_CREAT | O_TRUNC, A68_PROTECTION);
 540      ABEND (FILE_LISTING_FD (&A68_JOB) == -1, ERROR_ACTION, __func__);
 541      FILE_LISTING_OPENED (&A68_JOB) = A68_TRUE;
 542    } else {
 543      FILE_LISTING_OPENED (&A68_JOB) = A68_FALSE;
 544    }
 545  // Write listing.
 546    if (FILE_LISTING_OPENED (&A68_JOB)) {
 547      A68 (heap_is_fluid) = A68_TRUE;
 548      write_listing_header ();
 549      write_source_listing ();
 550      write_tree_listing ();
 551      if (ERROR_COUNT (&A68_JOB) == 0 && OPTION_OPT_LEVEL (&A68_JOB) > 0) {
 552        write_object_listing ();
 553      }
 554      write_listing ();
 555      ASSERT (close (FILE_LISTING_FD (&A68_JOB)) == 0);
 556      FILE_LISTING_OPENED (&A68_JOB) = A68_FALSE;
 557      verbosity ();
 558    }
 559  // Cleaning up the intermediate files.
 560  #if defined (BUILD_A68_COMPILER)
 561    announce_phase ("clean up intermediate files");
 562    if (OPTION_OPT_LEVEL (&A68_JOB) >= OPTIMISE_0 && OPTION_REGRESSION_TEST (&A68_JOB) && !OPTION_KEEP (&A68_JOB)) {
 563      if (emitted) {
 564        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 565      }
 566      a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 567    }
 568    if (OPTION_RUN_SCRIPT (&A68_JOB) && !OPTION_KEEP (&A68_JOB)) {
 569      if (emitted) {
 570        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 571      }
 572      a68_rm (FILE_SOURCE_NAME (&A68_JOB));
 573      a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 574    } else if (OPTION_COMPILE (&A68_JOB)) {
 575      build_script ();
 576      if (!OPTION_KEEP (&A68_JOB)) {
 577        if (emitted) {
 578          a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 579        }
 580        a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 581      }
 582    } else if (OPTION_OPT_LEVEL (&A68_JOB) == OPTIMISE_0 && !OPTION_KEEP (&A68_JOB)) {
 583      if (emitted) {
 584        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 585      }
 586      a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 587    } else if (OPTION_OPT_LEVEL (&A68_JOB) > OPTIMISE_0 && !OPTION_KEEP (&A68_JOB)) {
 588      if (emitted) {
 589        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 590      }
 591    } else if (OPTION_RERUN (&A68_JOB) && !OPTION_KEEP (&A68_JOB)) {
 592      if (emitted) {
 593        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 594      }
 595    }
 596  #endif
 597  }
 598  
 599  //! @brief Exit a68g in an orderly manner.
 600  
 601  void a68_exit (int code)
 602  {
 603    announce_phase ("exit");
 604  #if defined (HAVE_GNU_MPFR)
 605    mpfr_free_cache ();
 606  #endif
 607  // Close unclosed files, remove temp files.
 608    free_file_entries ();
 609  // Close the terminal.
 610    if (A68 (close_tty_on_exit) || OPTION_REGRESSION_TEST (&A68_JOB)) {
 611      io_close_tty_line ();
 612    } else if (OPTION_VERBOSE (&A68_JOB)) {
 613      io_close_tty_line ();
 614    }
 615  #if defined (HAVE_CURSES)
 616  // "curses" might still be open if it was not closed from A68, or the program
 617  // was interrupted, or a runtime error occured. That wreaks havoc on your
 618  // terminal. 
 619    genie_curses_end (NO_NODE);
 620  #endif
 621  // Clean up stale things.
 622    free_syntax_tree (TOP_NODE (&A68_JOB));
 623    free_option_list (OPTION_LIST (&A68_JOB));
 624    a68_free (A68 (node_register));
 625    a68_free (A68 (options));
 626  //
 627    discard_heap ();
 628  //
 629    a68_free (FILE_PATH (&A68_JOB));
 630    a68_free (FILE_INITIAL_NAME (&A68_JOB));
 631    a68_free (FILE_GENERIC_NAME (&A68_JOB));
 632    a68_free (FILE_SOURCE_NAME (&A68_JOB));
 633    a68_free (FILE_LISTING_NAME (&A68_JOB));
 634    a68_free (FILE_OBJECT_NAME (&A68_JOB));
 635    a68_free (FILE_PLUGIN_NAME (&A68_JOB));
 636    a68_free (FILE_BINARY_NAME (&A68_JOB));
 637    a68_free (FILE_PRETTY_NAME (&A68_JOB));
 638    a68_free (FILE_SCRIPT_NAME (&A68_JOB));
 639    a68_free (FILE_DIAGS_NAME (&A68_JOB));
 640  //
 641    a68_free (A68_MP (mp_one));
 642    a68_free (A68_MP (mp_pi));
 643    a68_free (A68_MP (mp_half_pi));
 644    a68_free (A68_MP (mp_two_pi));
 645    a68_free (A68_MP (mp_sqrt_two_pi));
 646    a68_free (A68_MP (mp_sqrt_pi));
 647    a68_free (A68_MP (mp_ln_pi));
 648    a68_free (A68_MP (mp_180_over_pi));
 649    a68_free (A68_MP (mp_pi_over_180));
 650  //
 651    exit (code);
 652  }
 653  
 654  //! @brief Main entry point.
 655  
 656  int main (int argc, char *argv[])
 657  {
 658    BYTE_T stack_offset;          // Leave this here!
 659    A68 (argc) = argc;
 660    A68 (argv) = argv;
 661    A68 (close_tty_on_exit) = A68_TRUE;
 662    FILE_DIAGS_FD (&A68_JOB) = -1;
 663  // Get command name and discard path.
 664    bufcpy (A68 (a68_cmd_name), argv[0], BUFFER_SIZE);
 665    for (int k = (int) strlen (A68 (a68_cmd_name)) - 1; k >= 0; k--) {
 666  #if defined (BUILD_WIN32)
 667      char delim = '\\';
 668  #else
 669      char delim = '/';
 670  #endif
 671      if (A68 (a68_cmd_name)[k] == delim) {
 672        MOVE (&A68 (a68_cmd_name)[0], &A68 (a68_cmd_name)[k + 1], (int) strlen (A68 (a68_cmd_name)) - k + 1);
 673        k = -1;
 674      }
 675    }
 676  // Try to read maximum line width on the terminal,
 677  // used to pretty print diagnostics to same.
 678    a68_getty (&A68 (term_heigth), &A68 (term_width));
 679  // Determine clock resolution.
 680    {
 681      clock_t t0 = clock (), t1;
 682      do {
 683        t1 = clock ();
 684      } while (t1 == t0);
 685      A68 (clock_res) = (t1 - t0) / (clock_t) CLOCKS_PER_SEC;
 686    }
 687  // Set the main thread id.
 688  #if defined (BUILD_PARALLEL_CLAUSE)
 689    A68_PAR (main_thread_id) = pthread_self ();
 690  #endif
 691    A68 (heap_is_fluid) = A68_TRUE;
 692    A68 (system_stack_offset) = &stack_offset;
 693    init_file_entries ();
 694    if (!setjmp (RENDEZ_VOUS (&A68_JOB))) {
 695      init_tty ();
 696  // Initialise option handling.
 697      init_options ();
 698      SOURCE_SCAN (&A68_JOB) = 1;
 699      default_options (&A68_JOB);
 700      default_mem_sizes (1);
 701  // Initialise core.
 702      A68_STACK = NO_BYTE;
 703      A68_HEAP = NO_BYTE;
 704      A68_HANDLES = NO_BYTE;
 705      get_stack_size ();
 706  // Well, let's start.
 707      TOP_REFINEMENT (&A68_JOB) = NO_REFINEMENT;
 708      FILE_INITIAL_NAME (&A68_JOB) = NO_TEXT;
 709      FILE_GENERIC_NAME (&A68_JOB) = NO_TEXT;
 710      FILE_SOURCE_NAME (&A68_JOB) = NO_TEXT;
 711      FILE_LISTING_NAME (&A68_JOB) = NO_TEXT;
 712      FILE_OBJECT_NAME (&A68_JOB) = NO_TEXT;
 713      FILE_PLUGIN_NAME (&A68_JOB) = NO_TEXT;
 714      FILE_BINARY_NAME (&A68_JOB) = NO_TEXT;
 715      FILE_PRETTY_NAME (&A68_JOB) = NO_TEXT;
 716      FILE_SCRIPT_NAME (&A68_JOB) = NO_TEXT;
 717      FILE_DIAGS_NAME (&A68_JOB) = NO_TEXT;
 718  // Options are processed here.
 719      read_rc_options ();
 720      read_env_options ();
 721  // Posix copies arguments from the command line.
 722      if (argc <= 1) {
 723        online_help (STDOUT_FILENO);
 724        a68_exit (EXIT_FAILURE);
 725      }
 726      for (int k = 1; k < argc; k++) {
 727        add_option_list (&(OPTION_LIST (&A68_JOB)), argv[k], NO_LINE);
 728      }
 729      if (!set_options (OPTION_LIST (&A68_JOB), A68_TRUE)) {
 730        a68_exit (EXIT_FAILURE);
 731      }
 732  // State license.
 733      if (OPTION_LICENSE (&A68_JOB)) {
 734        state_license (STDOUT_FILENO);
 735      }
 736  // State version.
 737      if (OPTION_VERSION (&A68_JOB)) {
 738        state_version (STDOUT_FILENO);
 739      }
 740  // Start the UI.
 741      init_before_tokeniser ();
 742  // Running a script.
 743  #if defined (BUILD_A68_COMPILER)
 744      if (OPTION_RUN_SCRIPT (&A68_JOB)) {
 745        load_script ();
 746      }
 747  #endif
 748  // We translate the program.
 749      if (FILE_INITIAL_NAME (&A68_JOB) == NO_TEXT || strlen (FILE_INITIAL_NAME (&A68_JOB)) == 0) {
 750        SCAN_ERROR (!(OPTION_LICENSE (&A68_JOB) || OPTION_VERSION (&A68_JOB)), NO_LINE, NO_TEXT, ERROR_NO_SOURCE_FILE);
 751      } else {
 752        compiler_interpreter ();
 753      }
 754      a68_exit (ERROR_COUNT (&A68_JOB) == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 755      return EXIT_SUCCESS;
 756    } else {
 757      diagnostics_to_terminal (TOP_LINE (&A68_JOB), A68_ALL_DIAGNOSTICS);
 758      a68_exit (EXIT_FAILURE);
 759      return EXIT_FAILURE;
 760    }
 761  }