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-2025 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 // --assertions, --noassertions, switch elaboration of assertions on or off.
27 // --backtrace, --nobacktrace, switch stack backtracing in case of a runtime error.
28 // --boldstropping, set stropping mode to bold stropping.
29 // --brackets, consider [ .. ] and { .. } as equivalent to ( .. ).
30 // --check, --norun, check syntax only, interpreter does not start.
31 // --clock, report execution time excluding compilation time.
32 // --compile, compile source file.
33 // --debug, --monitor, start execution in the debugger and debug in case of runtime error.
34 // --echo string, echo 'string' to standard output.
35 // --execute unit, execute algol 68 unit 'unit'.
36 // --exit, --, ignore next options.
37 // --extensive, make extensive listing.
38 // --file string, accept string as generic filename.
39 // --frame 'number', set frame stack size to 'number'.
40 // --handles 'number', set handle space size to 'number'.
41 // --heap 'number', set heap size to 'number'.
42 // --keep, --nokeep, switch object file deletion off or on.
43 // --listing, make concise listing.
44 // --moids, make overview of moids in listing file.
45 // -O0, -O1, -O2, -O3, switch compilation on and pass option to back-end C compiler.
46 // --optimise, --nooptimise, switch compilation on or off.
47 // --pedantic, equivalent to --warnings --portcheck.
48 // --portcheck, --noportcheck, switch portability warnings on or off.
49 // --pragmats, --nopragmats, switch elaboration of pragmat items on or off.
50 // --precision 'number', set precision for long long modes to 'number' significant digits.
51 // --preludelisting, make a listing of preludes.
52 // --pretty-print, pretty-print the source file.
53 // --print unit, print value yielded by algol 68 unit 'unit'.
54 // --quiet, suppresses all warning diagnostics.
55 // --quotestropping, set stropping mode to quote stropping.
56 // --reductions, print parser reductions.
57 // --run, override --check/--norun options.
58 // --rerun, run using already compiled code.
59 // --script, set next option as source file name; pass further options to algol 68 program.
60 // --source, --nosource, switch listing of source lines in listing file on or off.
61 // --stack 'number', set expression stack size to 'number'.
62 // --statistics, print statistics in listing file.
63 // --strict, disable most extensions to Algol 68 syntax.
64 // --timelimit 'number', interrupt the interpreter after 'number' seconds.
65 // --trace, --notrace, switch tracing of a running program on or off.
66 // --tree, --notree, switch syntax tree listing in listing file on or off.
67 // --unused, make an overview of unused tags in the listing file.
68 // --verbose, inform on program actions.
69 // --version, state version of the running copy.
70 // --warnings, --nowarnings, switch warning diagnostics on or off.
71 // --xref, --noxref, switch cross reference in the listing file on or off.
72
73 #include "a68g.h"
74 #include "a68g-listing.h"
75 #include "a68g-mp.h"
76 #include "a68g-optimiser.h"
77 #include "a68g-options.h"
78 #include "a68g-parser.h"
79 #include "a68g-postulates.h"
80 #include "a68g-genie.h"
81 #include "a68g-prelude.h"
82 #include "a68g-prelude-mathlib.h"
83
84 #if defined (HAVE_MATHLIB)
85 #include <Rmath.h>
86 #endif
87
88 #if defined (HAVE_CURL)
89 #include <curl/curl.h>
90 #endif
91
92 GLOBALS_T common;
93
94 #define EXTENSIONS 13
95 static char *extensions[EXTENSIONS] = {
96 NO_TEXT,
97 ".a68", ".A68",
98 ".a68g", ".A68G",
99 ".alg", ".ALG",
100 ".algol", ".ALGOL",
101 ".algol68", ".ALGOL68",
102 ".algol68g", ".ALGOL68G"
103 };
104
105 void compiler_interpreter (void);
106
107 //! @brief Verbose statistics, only useful when debugging a68g.
108
109 void verbosity (void)
110 {
111 #if defined (A68G_DEBUG)
112 ;
113 #else
114 ;
115 #endif
116 }
117
118 //! @brief State license of running a68g image.
119
120 void state_license (FILE_T f)
121 {
122 #define PR(s)\
123 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s\n", (s)) >= 0);\
124 WRITE (f, A68G (output_line));
125
126 if (f == A68G_STDOUT) {
127 io_close_tty_line ();
128 }
129 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "Algol 68 Genie %s\n", PACKAGE_VERSION) >= 0);
130 WRITE (f, A68G (output_line));
131 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "Copyright 2001-2025 %s.\n", PACKAGE_BUGREPORT) >= 0);
132 WRITE (f, A68G (output_line));
133 PR ("");
134 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "This is free software covered by the GNU General Public License.\n") >= 0);
135 WRITE (f, A68G (output_line));
136 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "There is ABSOLUTELY NO WARRANTY for Algol 68 Genie;\n") >= 0);
137 WRITE (f, A68G (output_line));
138 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n") >= 0);
139 WRITE (f, A68G (output_line));
140 PR ("See the GNU General Public License for more details.");
141 PR ("");
142 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "Please report bugs to %s.\n", PACKAGE_BUGREPORT) >= 0);
143 WRITE (f, A68G (output_line));
144 #undef PR
145 }
146
147 //! @brief State version of running a68g image.
148
149 void state_version (FILE_T f)
150 {
151 #define PR(s)\
152 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s\n", (s)) >= 0);\
153 WRITE (f, A68G (output_line));
154
155 if (f == A68G_STDOUT) {
156 io_close_tty_line ();
157 }
158 state_license (f);
159 PR ("");
160 #if defined (BUILD_WIN32)
161 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "WIN32 executable\n") >= 0);
162 WRITE (f, A68G (output_line));
163 WRITELN (f, "");
164 #endif
165 #if defined (BUILD_WIN64)
166 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "WIN64 executable\n") >= 0);
167 WRITE (f, A68G (output_line));
168 WRITELN (f, "");
169 #endif
170 #if (A68G_LEVEL >= 3)
171 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With hardware support for long modes\n") >= 0);
172 WRITE (f, A68G (output_line));
173 #endif
174 #if defined (BUILD_A68G_COMPILER)
175 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With plugin-compilation support\n") >= 0);
176 WRITE (f, A68G (output_line));
177 #endif
178 #if defined (BUILD_PARALLEL_CLAUSE)
179 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With parallel-clause support\n") >= 0);
180 WRITE (f, A68G (output_line));
181 #endif
182 #if defined (HAVE_POSTGRESQL)
183 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With PostgreSQL support\n") >= 0);
184 WRITE (f, A68G (output_line));
185 #endif
186 #if defined (HAVE_CURL)
187 curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
188 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With curl %s\n", data->version) >= 0);
189 WRITE (f, A68G (output_line));
190 #endif
191 #if defined (HAVE_GNU_MPFR)
192 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With GNU MP %s\n", gmp_version) >= 0);
193 WRITE (f, A68G (output_line));
194 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With GNU MPFR %s\n", mpfr_get_version ()) >= 0);
195 WRITE (f, A68G (output_line));
196 #endif
197 #if defined (HAVE_MATHLIB)
198 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With mathlib from R %s\n", R_VERSION_STRING) >= 0);
199 WRITE (f, A68G (output_line));
200 #endif
201 #if defined (HAVE_GSL)
202 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With GNU Scientific Library %s\n", GSL_VERSION) >= 0);
203 WRITE (f, A68G (output_line));
204 #endif
205 #if defined (HAVE_GNU_PLOTUTILS)
206 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With GNU plotutils %s\n", PL_LIBPLOT_VER_STRING) >= 0);
207 WRITE (f, A68G (output_line));
208 #endif
209 #if defined (HAVE_CURSES)
210 #if defined (NCURSES_VERSION)
211 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With ncurses %s\n", NCURSES_VERSION) >= 0);
212 #else
213 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "With curses support\n") >= 0);
214 #endif
215 WRITE (f, A68G (output_line));
216 #endif
217 #if defined (_CS_GNU_LIBC_VERSION) && defined (BUILD_UNIX)
218 if (confstr (_CS_GNU_LIBC_VERSION, A68G (input_line), BUFFER_SIZE) > (size_t) 0) {
219 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "GNU libc version %s\n", A68G (input_line)) >= 0);
220 WRITE (f, A68G (output_line));
221 }
222 #if (defined (BUILD_PARALLEL_CLAUSE) && defined (_CS_GNU_LIBPTHREAD_VERSION))
223 if (confstr (_CS_GNU_LIBPTHREAD_VERSION, A68G (input_line), BUFFER_SIZE) > (size_t) 0) {
224 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "GNU libpthread version %s\n", A68G (input_line)) >= 0);
225 WRITE (f, A68G (output_line));
226 }
227 #endif
228 #endif
229 #define RSIZE(n) (unt) (sizeof (n) / sizeof (int))
230 #if defined (BUILD_A68G_COMPILER) && defined (C_COMPILER)
231 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "Build level %d.%x%x%x%x %s %s\n", A68G_LEVEL, RSIZE (INT_T), RSIZE (REAL_T), RSIZE (MP_INT_T), RSIZE (MP_REAL_T), C_COMPILER, __DATE__) >= 0);
232 #else
233 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "Build level %d.%x%x%x%x %s\n", A68G_LEVEL, RSIZE (INT_T), RSIZE (REAL_T), RSIZE (MP_INT_T), RSIZE (MP_REAL_T), __DATE__) >= 0);
234 #endif
235 #undef RSIZE
236 WRITE (f, A68G (output_line));
237 #undef PR
238 }
239
240 //! @brief Give brief help if someone types 'a68g --help'.
241
242 void online_help (FILE_T f)
243 {
244 if (f == A68G_STDOUT) {
245 io_close_tty_line ();
246 }
247 state_license (f);
248 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "Usage: %s [options | filename]", A68G (a68g_cmd_name)) >= 0);
249 WRITELN (f, A68G (output_line));
250 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "For help: %s --apropos [keyword]", A68G (a68g_cmd_name)) >= 0);
251 WRITELN (f, A68G (output_line));
252 }
253
254 //! @brief Start book keeping for a phase.
255
256 void announce_phase (char *t)
257 {
258 if (OPTION_VERBOSE (&A68G_JOB)) {
259 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: %s", A68G (a68g_cmd_name), t) >= 0);
260 io_close_tty_line ();
261 WRITE (A68G_STDOUT, A68G (output_line));
262 }
263 }
264
265 //! @brief Test extension and strip.
266
267 BOOL_T strip_extension (char *ext)
268 {
269 if (ext == NO_TEXT) {
270 return A68G_FALSE;
271 }
272 size_t nlen = strlen (FILE_SOURCE_NAME (&A68G_JOB)), xlen = strlen (ext);
273 if (nlen > xlen && strcmp (&(FILE_SOURCE_NAME (&A68G_JOB)[nlen - xlen]), ext) == 0) {
274 char *fn = (char *) get_heap_space (nlen + 1);
275 a68g_bufcpy (fn, FILE_SOURCE_NAME (&A68G_JOB), nlen);
276 fn[nlen - xlen] = NULL_CHAR;
277 a68g_free (FILE_GENERIC_NAME (&A68G_JOB));
278 FILE_GENERIC_NAME (&A68G_JOB) = new_string (fn, NO_TEXT);
279 a68g_free (fn);
280 return A68G_TRUE;
281 } else {
282 return A68G_FALSE;
283 }
284 }
285
286 //! @brief Try opening with an extension.
287
288 void open_with_extensions (void)
289 {
290 FILE_SOURCE_FD (&A68G_JOB) = -1;
291 for (int k = 0; k < EXTENSIONS && FILE_SOURCE_FD (&A68G_JOB) == -1; k++) {
292 size_t len;
293 char *fn = NULL;
294 if (extensions[k] == NO_TEXT) {
295 len = strlen (FILE_INITIAL_NAME (&A68G_JOB)) + 1;
296 fn = (char *) get_heap_space (len);
297 a68g_bufcpy (fn, FILE_INITIAL_NAME (&A68G_JOB), len);
298 } else {
299 len = strlen (FILE_INITIAL_NAME (&A68G_JOB)) + strlen (extensions[k]) + 1;
300 fn = (char *) get_heap_space (len);
301 a68g_bufcpy (fn, FILE_INITIAL_NAME (&A68G_JOB), len);
302 a68g_bufcat (fn, extensions[k], len);
303 }
304 FILE_SOURCE_FD (&A68G_JOB) = open (fn, O_RDONLY | O_BINARY);
305 if (FILE_SOURCE_FD (&A68G_JOB) != -1) {
306 BOOL_T cont = A68G_TRUE;
307 a68g_free (FILE_SOURCE_NAME (&A68G_JOB));
308 a68g_free (FILE_GENERIC_NAME (&A68G_JOB));
309 FILE_SOURCE_NAME (&A68G_JOB) = new_string (fn, NO_TEXT);
310 FILE_GENERIC_NAME (&A68G_JOB) = new_string (a68g_basename (fn), NO_TEXT);
311 FILE_PATH (&A68G_JOB) = new_string (a68g_dirname (fn), NO_TEXT);
312 for (int l = 0; l < EXTENSIONS && cont; l++) {
313 if (strip_extension (extensions[l])) {
314 cont = A68G_FALSE;
315 }
316 }
317 }
318 a68g_free (fn);
319 }
320 }
321
322 //! @brief Remove a regular file.
323
324 void a68g_rm (char *fn)
325 {
326 struct stat path_stat;
327 if (stat (fn, &path_stat) == 0) {
328 if (S_ISREG (path_stat.st_mode)) {
329 ABEND (remove (fn) != 0, ERROR_ACTION, FILE_OBJECT_NAME (&A68G_JOB));
330 }
331 }
332 }
333
334 //! @brief Drives compilation and interpretation.
335
336 void compiler_interpreter (void)
337 {
338 BOOL_T emitted = A68G_FALSE;
339 TREE_LISTING_SAFE (&A68G_JOB) = A68G_FALSE;
340 CROSS_REFERENCE_SAFE (&A68G_JOB) = A68G_FALSE;
341 A68G (in_execution) = A68G_FALSE;
342 A68G (new_nodes) = 0;
343 A68G (new_modes) = 0;
344 A68G (new_postulates) = 0;
345 A68G (new_node_infos) = 0;
346 A68G (new_genie_infos) = 0;
347 A68G (symbol_table_count) = 0;
348 A68G (mode_count) = 0;
349 A68G (node_register) = NO_REF;
350 init_postulates ();
351 A68G (do_confirm_exit) = A68G_TRUE;
352 A68G (f_entry) = NO_NODE;
353 A68G (global_level) = 0;
354 A68G (max_lex_lvl) = 0;
355 A68G_PARSER (stop_scanner) = A68G_FALSE;
356 A68G_PARSER (read_error) = A68G_FALSE;
357 A68G_PARSER (no_preprocessing) = A68G_FALSE;
358 A68G_PARSER (reductions) = 0;
359 A68G_PARSER (tag_number) = 0;
360 A68G (curses_mode) = A68G_FALSE;
361 A68G (top_soid_list) = NO_SOID;
362 A68G (max_simplout_size) = 0;
363 A68G_MON (in_monitor) = A68G_FALSE;
364 A68G_MP (mp_ln_scale_size) = -1;
365 A68G_MP (mp_ln_10_size) = -1;
366 A68G_MP (mp_gamma_size) = -1;
367 A68G_MP (mp_one_size) = -1;
368 A68G_MP (mp_pi_size) = -1;
369 // File set-up.
370 SCAN_ERROR (FILE_INITIAL_NAME (&A68G_JOB) == NO_TEXT, NO_LINE, NO_TEXT, ERROR_NO_SOURCE_FILE);
371 FILE_BINARY_OPENED (&A68G_JOB) = A68G_FALSE;
372 FILE_BINARY_WRITEMOOD (&A68G_JOB) = A68G_TRUE;
373 FILE_PLUGIN_OPENED (&A68G_JOB) = A68G_FALSE;
374 FILE_PLUGIN_WRITEMOOD (&A68G_JOB) = A68G_TRUE;
375 FILE_LISTING_OPENED (&A68G_JOB) = A68G_FALSE;
376 FILE_LISTING_WRITEMOOD (&A68G_JOB) = A68G_TRUE;
377 FILE_OBJECT_OPENED (&A68G_JOB) = A68G_FALSE;
378 FILE_OBJECT_WRITEMOOD (&A68G_JOB) = A68G_TRUE;
379 FILE_PRETTY_OPENED (&A68G_JOB) = A68G_FALSE;
380 FILE_SCRIPT_OPENED (&A68G_JOB) = A68G_FALSE;
381 FILE_SCRIPT_WRITEMOOD (&A68G_JOB) = A68G_FALSE;
382 FILE_SOURCE_OPENED (&A68G_JOB) = A68G_FALSE;
383 FILE_SOURCE_WRITEMOOD (&A68G_JOB) = A68G_FALSE;
384 FILE_DIAGS_OPENED (&A68G_JOB) = A68G_FALSE;
385 FILE_DIAGS_WRITEMOOD (&A68G_JOB) = A68G_TRUE;
386 // Open the source file.
387 // Open it for binary reading for systems that require so (Win32).
388 // Accept various silent extensions.
389 errno = 0;
390 FILE_SOURCE_NAME (&A68G_JOB) = NO_TEXT;
391 FILE_GENERIC_NAME (&A68G_JOB) = NO_TEXT;
392 open_with_extensions ();
393 if (FILE_SOURCE_NAME (&A68G_JOB) == NO_TEXT) {
394 errno = ENOENT;
395 SCAN_ERROR (A68G_TRUE, NO_LINE, NO_TEXT, ERROR_SOURCE_FILE_OPEN);
396 } else {
397 struct stat path_stat;
398 errno = 0;
399 SCAN_ERROR (stat (FILE_SOURCE_NAME (&A68G_JOB), &path_stat) != 0, NO_LINE, NO_TEXT, ERROR_SOURCE_FILE_OPEN);
400 SCAN_ERROR (S_ISDIR (path_stat.st_mode), NO_LINE, NO_TEXT, ERROR_IS_DIRECTORY);
401 SCAN_ERROR (!S_ISREG (path_stat.st_mode), NO_LINE, NO_TEXT, ERROR_NO_REGULAR_FILE);
402 }
403 if (FILE_SOURCE_FD (&A68G_JOB) == -1) {
404 scan_error (NO_LINE, NO_TEXT, ERROR_SOURCE_FILE_OPEN);
405 }
406 ABEND (FILE_SOURCE_NAME (&A68G_JOB) == NO_TEXT, ERROR_INTERNAL_CONSISTENCY, __func__);
407 ABEND (FILE_GENERIC_NAME (&A68G_JOB) == NO_TEXT, ERROR_INTERNAL_CONSISTENCY, __func__);
408 // Object file.
409 size_t len = 1 + strlen (FILE_GENERIC_NAME (&A68G_JOB)) + strlen (OBJECT_EXTENSION);
410 FILE_OBJECT_NAME (&A68G_JOB) = (char *) get_heap_space (len);
411 a68g_bufcpy (FILE_OBJECT_NAME (&A68G_JOB), FILE_GENERIC_NAME (&A68G_JOB), len);
412 a68g_bufcat (FILE_OBJECT_NAME (&A68G_JOB), OBJECT_EXTENSION, len);
413 // Binary.
414 len = 1 + strlen (FILE_GENERIC_NAME (&A68G_JOB)) + strlen (PLUGIN_EXTENSION);
415 FILE_BINARY_NAME (&A68G_JOB) = (char *) get_heap_space (len);
416 a68g_bufcpy (FILE_BINARY_NAME (&A68G_JOB), FILE_GENERIC_NAME (&A68G_JOB), len);
417 a68g_bufcat (FILE_BINARY_NAME (&A68G_JOB), BINARY_EXTENSION, len);
418 // Library file.
419 len = 1 + strlen (FILE_GENERIC_NAME (&A68G_JOB)) + strlen (PLUGIN_EXTENSION);
420 FILE_PLUGIN_NAME (&A68G_JOB) = (char *) get_heap_space (len);
421 a68g_bufcpy (FILE_PLUGIN_NAME (&A68G_JOB), FILE_GENERIC_NAME (&A68G_JOB), len);
422 a68g_bufcat (FILE_PLUGIN_NAME (&A68G_JOB), PLUGIN_EXTENSION, len);
423 // Listing file.
424 len = 1 + strlen (FILE_GENERIC_NAME (&A68G_JOB)) + strlen (LISTING_EXTENSION);
425 FILE_LISTING_NAME (&A68G_JOB) = (char *) get_heap_space (len);
426 a68g_bufcpy (FILE_LISTING_NAME (&A68G_JOB), FILE_GENERIC_NAME (&A68G_JOB), len);
427 a68g_bufcat (FILE_LISTING_NAME (&A68G_JOB), LISTING_EXTENSION, len);
428 // Pretty file.
429 len = 1 + strlen (FILE_GENERIC_NAME (&A68G_JOB)) + strlen (PRETTY_EXTENSION);
430 FILE_PRETTY_NAME (&A68G_JOB) = (char *) get_heap_space (len);
431 a68g_bufcpy (FILE_PRETTY_NAME (&A68G_JOB), FILE_GENERIC_NAME (&A68G_JOB), len);
432 a68g_bufcat (FILE_PRETTY_NAME (&A68G_JOB), PRETTY_EXTENSION, len);
433 // Script file.
434 len = 1 + strlen (FILE_GENERIC_NAME (&A68G_JOB)) + strlen (SCRIPT_EXTENSION);
435 FILE_SCRIPT_NAME (&A68G_JOB) = (char *) get_heap_space (len);
436 a68g_bufcpy (FILE_SCRIPT_NAME (&A68G_JOB), FILE_GENERIC_NAME (&A68G_JOB), len);
437 a68g_bufcat (FILE_SCRIPT_NAME (&A68G_JOB), SCRIPT_EXTENSION, len);
438 // Parser.
439 a68g_parser ();
440 if (TOP_NODE (&A68G_JOB) == NO_NODE) {
441 errno = ECANCELED;
442 ABEND (A68G_TRUE, ERROR_SOURCE_FILE_EMPTY, NO_TEXT);
443 }
444 // Portability checker.
445 if (ERROR_COUNT (&A68G_JOB) == 0) {
446 announce_phase ("portability checker");
447 portcheck (TOP_NODE (&A68G_JOB));
448 verbosity ();
449 }
450 // Finalise syntax tree.
451 if (ERROR_COUNT (&A68G_JOB) == 0) {
452 int num = 0;
453 renumber_nodes (TOP_NODE (&A68G_JOB), &num);
454 NEST (TABLE (TOP_NODE (&A68G_JOB))) = A68G (symbol_table_count) = 3;
455 reset_symbol_table_nest_count (TOP_NODE (&A68G_JOB));
456 verbosity ();
457 }
458 if (A68G_MP (varying_mp_digits) > width_to_mp_digits (MP_MAX_DECIMALS)) {
459 diagnostic (A68G_WARNING, NO_NODE, WARNING_PRECISION, NO_LINE, 0, A68G_MP (varying_mp_digits) * LOG_MP_RADIX);
460 }
461 // Plugin code generation and compilation.
462 if (ERROR_COUNT (&A68G_JOB) == 0 && OPTION_OPT_LEVEL (&A68G_JOB) > NO_OPTIMISE) {
463 announce_phase ("plugin-compiler");
464 plugin_driver_code ();
465 #if defined (BUILD_A68G_COMPILER)
466 emitted = A68G_TRUE;
467 if (ERROR_COUNT (&A68G_JOB) == 0 && !OPTION_RUN_SCRIPT (&A68G_JOB)) {
468 plugin_driver_compile ();
469 }
470 verbosity ();
471 #else
472 emitted = A68G_FALSE;
473 diagnostic (A68G_WARNING, TOP_NODE (&A68G_JOB), WARNING_OPTIMISATION);
474 #endif
475 }
476 // Indenter.
477 if (ERROR_COUNT (&A68G_JOB) == 0 && OPTION_PRETTY (&A68G_JOB)) {
478 announce_phase ("indenter");
479 indenter (&A68G_JOB);
480 verbosity ();
481 }
482 // Interpreter initialisation.
483 diagnostics_to_terminal (TOP_LINE (&A68G_JOB), A68G_ALL_DIAGNOSTICS);
484 if (OPTION_DEBUG (&A68G_JOB)) {
485 state_license (A68G_STDOUT);
486 }
487 if (ERROR_COUNT (&A68G_JOB) == 0 && OPTION_COMPILE (&A68G_JOB) == A68G_FALSE &&
488 (OPTION_CHECK_ONLY (&A68G_JOB) ? OPTION_RUN (&A68G_JOB) : A68G_TRUE)) {
489 announce_phase ("genie");
490 GetRNGstate ();
491 A68G (f_entry) = TOP_NODE (&A68G_JOB);
492 A68G (close_tty_on_exit) = A68G_FALSE;
493 #if defined (BUILD_A68G_COMPILER)
494 plugin_driver_genie ();
495 #else
496 genie ((void *) NULL);
497 #endif
498 // Free heap allocated by genie.
499 if (A68G_GC (total) > 10 * MEGABYTE) {
500 ASSERT (a68g_bufprt (A68G (edit_line), SNPRINTF_SIZE,
501 "sweeps=" A68G_LU ", refused=" A68G_LU ", freed=" A68G_LU "MB",
502 A68G_GC (sweeps), A68G_GC (refused), A68G_GC (total) / MEGABYTE)
503 );
504 } else if (A68G_GC (total) > 10 * KILOBYTE) {
505 ASSERT (a68g_bufprt (A68G (edit_line), SNPRINTF_SIZE,
506 "sweeps=" A68G_LU ", refused=" A68G_LU ", freed=" A68G_LU "kB",
507 A68G_GC (sweeps), A68G_GC (refused), A68G_GC (total) / KILOBYTE)
508 );
509 } else {
510 ASSERT (a68g_bufprt (A68G (edit_line), SNPRINTF_SIZE,
511 "sweeps=" A68G_LU ", refused=" A68G_LU ", freed=" A68G_LU,
512 A68G_GC (sweeps), A68G_GC (refused), A68G_GC (total))
513 );
514 }
515 announce_phase (A68G (edit_line));
516 genie_free (TOP_NODE (&A68G_JOB));
517 // Store seed for rng.
518 announce_phase ("store rng state");
519 PutRNGstate ();
520 // Normal end of program.
521 diagnostics_to_terminal (TOP_LINE (&A68G_JOB), A68G_RUNTIME_ERROR);
522 if (OPTION_DEBUG (&A68G_JOB) || OPTION_TRACE (&A68G_JOB) || OPTION_CLOCK (&A68G_JOB)) {
523 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "\nGenie finished in %.2f seconds\n", seconds () - A68G (cputime_0)) >= 0);
524 WRITE (A68G_STDOUT, A68G (output_line));
525 }
526 verbosity ();
527 }
528 // Setting up listing file.
529 announce_phase ("write listing");
530 if (OPTION_MOID_LISTING (&A68G_JOB) || OPTION_TREE_LISTING (&A68G_JOB) || OPTION_SOURCE_LISTING (&A68G_JOB) || OPTION_OBJECT_LISTING (&A68G_JOB) || OPTION_STATISTICS_LISTING (&A68G_JOB)) {
531 FILE_LISTING_FD (&A68G_JOB) = open (FILE_LISTING_NAME (&A68G_JOB), O_WRONLY | O_CREAT | O_TRUNC, A68G_PROTECTION);
532 ABEND (FILE_LISTING_FD (&A68G_JOB) == -1, ERROR_ACTION, __func__);
533 FILE_LISTING_OPENED (&A68G_JOB) = A68G_TRUE;
534 } else {
535 FILE_LISTING_OPENED (&A68G_JOB) = A68G_FALSE;
536 }
537 // Write listing.
538 if (FILE_LISTING_OPENED (&A68G_JOB)) {
539 A68G (heap_is_fluid) = A68G_TRUE;
540 write_listing_header ();
541 write_source_listing ();
542 write_tree_listing ();
543 if (ERROR_COUNT (&A68G_JOB) == 0 && OPTION_OPT_LEVEL (&A68G_JOB) > 0) {
544 write_object_listing ();
545 }
546 write_listing ();
547 ASSERT (close (FILE_LISTING_FD (&A68G_JOB)) == 0);
548 FILE_LISTING_OPENED (&A68G_JOB) = A68G_FALSE;
549 verbosity ();
550 }
551 // Cleaning up the intermediate files.
552 #if defined (BUILD_A68G_COMPILER)
553 announce_phase ("clean up intermediate files");
554 plugin_driver_clean (emitted);
555 #else
556 (void) emitted;
557 #endif
558 }
559
560 //! @brief Exit a68g in an orderly manner.
561
562 void a68g_exit (int code)
563 {
564 announce_phase ("exit");
565 #if defined (HAVE_GNU_MPFR)
566 mpfr_free_cache ();
567 #endif
568 // Close unclosed files, remove temp files.
569 free_file_entries ();
570 // Close the terminal.
571 if (A68G (close_tty_on_exit) || OPTION_REGRESSION_TEST (&A68G_JOB)) {
572 io_close_tty_line ();
573 } else if (OPTION_VERBOSE (&A68G_JOB)) {
574 io_close_tty_line ();
575 }
576 #if defined (HAVE_CURSES)
577 // "curses" might still be open if it was not closed from A68, or the program
578 // was interrupted, or a runtime error occured. That wreaks havoc on your
579 // terminal.
580 genie_curses_end (NO_NODE);
581 #endif
582 // Clean up stale things.
583 free_syntax_tree (TOP_NODE (&A68G_JOB));
584 free_option_list (OPTION_LIST (&A68G_JOB));
585 a68g_free (A68G (node_register));
586 a68g_free (A68G (options));
587 discard_heap ();
588 a68g_free (FILE_PATH (&A68G_JOB));
589 a68g_free (FILE_INITIAL_NAME (&A68G_JOB));
590 a68g_free (FILE_GENERIC_NAME (&A68G_JOB));
591 a68g_free (FILE_SOURCE_NAME (&A68G_JOB));
592 a68g_free (FILE_LISTING_NAME (&A68G_JOB));
593 a68g_free (FILE_OBJECT_NAME (&A68G_JOB));
594 a68g_free (FILE_PLUGIN_NAME (&A68G_JOB));
595 a68g_free (FILE_BINARY_NAME (&A68G_JOB));
596 a68g_free (FILE_PRETTY_NAME (&A68G_JOB));
597 a68g_free (FILE_SCRIPT_NAME (&A68G_JOB));
598 a68g_free (FILE_DIAGS_NAME (&A68G_JOB));
599 a68g_free (A68G_MP (mp_one));
600 a68g_free (A68G_MP (mp_pi));
601 a68g_free (A68G_MP (mp_half_pi));
602 a68g_free (A68G_MP (mp_two_pi));
603 a68g_free (A68G_MP (mp_sqrt_two_pi));
604 a68g_free (A68G_MP (mp_sqrt_pi));
605 a68g_free (A68G_MP (mp_ln_pi));
606 a68g_free (A68G_MP (mp_180_over_pi));
607 a68g_free (A68G_MP (mp_pi_over_180));
608 exit (code);
609 }
610
611 //! @brief Main entry point.
612
613 int main (int argc, char *argv[])
614 {
615 BYTE_T stack_offset; // Leave this here!
616 A68G (argc) = argc;
617 A68G (argv) = argv;
618 A68G (close_tty_on_exit) = A68G_TRUE;
619 FILE_DIAGS_FD (&A68G_JOB) = -1;
620 // Get command name and discard path.
621 #if defined (BUILD_WIN32) || defined (BUILD_WIN64)
622 a68g_bufcpy (A68G (a68g_cmd_name), "a68g", BUFFER_SIZE);
623 #else
624 a68g_bufcpy (A68G (a68g_cmd_name), argv[0], BUFFER_SIZE);
625 for (int k = strlen (A68G (a68g_cmd_name)) - 1; k >= 0; k--) {
626 if (A68G (a68g_cmd_name)[k] == '/') {
627 MOVE (&A68G (a68g_cmd_name)[0], &A68G (a68g_cmd_name)[k + 1], strlen (A68G (a68g_cmd_name)) - k + 1);
628 k = -1;
629 }
630 }
631 #endif
632 // Try to read maximum line width on the terminal,
633 // used to pretty print diagnostics to same.
634 a68g_getty (&A68G (term_heigth), &A68G (term_width));
635 // Determine clock resolution.
636 {
637 clock_t t0 = clock (), t1;
638 do {
639 t1 = clock ();
640 } while (t1 == t0);
641 A68G (clock_res) = (t1 - t0) / (clock_t) CLOCKS_PER_SEC;
642 }
643 // Set the main thread id.
644 #if defined (BUILD_PARALLEL_CLAUSE)
645 A68G_PAR (main_thread_id) = pthread_self ();
646 #endif
647 A68G (heap_is_fluid) = A68G_TRUE;
648 A68G (system_stack_offset) = &stack_offset;
649 init_file_entries ();
650 if (!setjmp (RENDEZ_VOUS (&A68G_JOB))) {
651 init_tty ();
652 // Initialise option handling.
653 init_options ();
654 SOURCE_SCAN (&A68G_JOB) = 1;
655 default_options (&A68G_JOB);
656 default_mem_sizes (1, NULL, NULL);
657 // Initialise core.
658 A68G_STACK = NO_BYTE;
659 A68G_HEAP = NO_BYTE;
660 A68G_HANDLES = NO_BYTE;
661 get_stack_size ();
662 // Well, let's start.
663 TOP_REFINEMENT (&A68G_JOB) = NO_REFINEMENT;
664 FILE_INITIAL_NAME (&A68G_JOB) = NO_TEXT;
665 FILE_GENERIC_NAME (&A68G_JOB) = NO_TEXT;
666 FILE_SOURCE_NAME (&A68G_JOB) = NO_TEXT;
667 FILE_LISTING_NAME (&A68G_JOB) = NO_TEXT;
668 FILE_OBJECT_NAME (&A68G_JOB) = NO_TEXT;
669 FILE_PLUGIN_NAME (&A68G_JOB) = NO_TEXT;
670 FILE_BINARY_NAME (&A68G_JOB) = NO_TEXT;
671 FILE_PRETTY_NAME (&A68G_JOB) = NO_TEXT;
672 FILE_SCRIPT_NAME (&A68G_JOB) = NO_TEXT;
673 FILE_DIAGS_NAME (&A68G_JOB) = NO_TEXT;
674 // Options are processed here.
675 read_rc_options ();
676 read_env_options ();
677 // Posix copies arguments from the command line.
678 if (argc <= 1) {
679 online_help (A68G_STDOUT);
680 a68g_exit (EXIT_FAILURE);
681 }
682 for (int k = 1; k < argc; k++) {
683 add_option_list (&(OPTION_LIST (&A68G_JOB)), argv[k], NO_LINE);
684 }
685 if (!set_options (OPTION_LIST (&A68G_JOB), A68G_TRUE)) {
686 a68g_exit (EXIT_FAILURE);
687 }
688 // State license.
689 if (OPTION_LICENSE (&A68G_JOB)) {
690 state_license (A68G_STDOUT);
691 }
692 // State version.
693 if (OPTION_VERSION (&A68G_JOB)) {
694 state_version (A68G_STDOUT);
695 }
696 // Start the UI.
697 init_before_tokeniser ();
698 // Running a script.
699 #if defined (BUILD_A68G_COMPILER)
700 if (OPTION_RUN_SCRIPT (&A68G_JOB)) {
701 load_script ();
702 }
703 #endif
704 // We translate the program.
705 if (FILE_INITIAL_NAME (&A68G_JOB) == NO_TEXT || strlen (FILE_INITIAL_NAME (&A68G_JOB)) == 0) {
706 SCAN_ERROR (!(OPTION_LICENSE (&A68G_JOB) || OPTION_VERSION (&A68G_JOB)), NO_LINE, NO_TEXT, ERROR_NO_SOURCE_FILE);
707 } else {
708 compiler_interpreter ();
709 }
710 a68g_exit (ERROR_COUNT (&A68G_JOB) == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
711 return EXIT_SUCCESS;
712 } else {
713 diagnostics_to_terminal (TOP_LINE (&A68G_JOB), A68G_ALL_DIAGNOSTICS);
714 a68g_exit (EXIT_FAILURE);
715 return EXIT_FAILURE;
716 }
717 }
© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)
|