diff options
author | Diego Novillo <dnovillo@gcc.gnu.org> | 2004-05-13 02:41:07 -0400 |
---|---|---|
committer | Diego Novillo <dnovillo@gcc.gnu.org> | 2004-05-13 02:41:07 -0400 |
commit | 6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f (patch) | |
tree | a2568888a519c077427b133de9ece5879a8484a5 /gcc/fortran/module.c | |
parent | ac1a20aec53364d77f3bdff94a2a0a06840e0fe9 (diff) | |
download | gcc-6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f.zip gcc-6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f.tar.gz gcc-6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f.tar.bz2 |
Merge tree-ssa-20020619-branch into mainline.
From-SVN: r81764
Diffstat (limited to 'gcc/fortran/module.c')
-rw-r--r-- | gcc/fortran/module.c | 3459 |
1 files changed, 3459 insertions, 0 deletions
diff --git a/gcc/fortran/module.c b/gcc/fortran/module.c new file mode 100644 index 0000000..3498f75 --- /dev/null +++ b/gcc/fortran/module.c @@ -0,0 +1,3459 @@ +/* Handle modules, which amounts to loading and saving symbols and + their attendant structures. + Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Contributed by Andy Vaught + +This file is part of GNU G95. + +GNU G95 is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU G95 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU G95; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* The syntax of g95 modules resembles that of lisp lists, ie a + sequence of atoms, which can be left or right parenthesis, names, + integers or strings. Parenthesis are always matched which allows + us to skip over sections at high speed without having to know + anything about the internal structure of the lists. A "name" is + usually a fortran 95 identifier, but can also start with '@' in + order to reference a hidden symbol. + + The first line of a module is an informational message about what + created the module, the file it came from and when it was created. + The second line is a warning for people not to edit the module. + The rest of the module looks like: + + ( ( <Interface info for UPLUS> ) + ( <Interface info for UMINUS> ) + ... + ) + ( ( <name of operator interface> <module of op interface> <i/f1> ... ) + ... + ) + ( ( <name of generic interface> <module of generic interface> <i/f1> ... ) + ... + ) + ( <Symbol Number (in no particular order)> + <True name of symbol> + <Module name of symbol> + ( <symbol information> ) + ... + ) + ( <Symtree name> + <Ambiguous flag> + <Symbol number> + ... + ) + + In general, symbols refer to other symbols by their symbol number, + which are zero based. Symbols are written to the module in no + particular order. */ + +#include "config.h" +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> + +#include "gfortran.h" +#include "match.h" +#include "parse.h" /* FIXME */ + +#define MODULE_EXTENSION ".mod" + + +/* Structure that descibes a position within a module file */ + +typedef struct +{ + int column, line; + fpos_t pos; +} +module_locus; + + +typedef enum +{ + P_UNKNOWN = 0, P_OTHER, P_NAMESPACE, P_COMPONENT, P_SYMBOL +} +pointer_t; + +/* The fixup structure lists pointers to pointers that have to + be updated when a pointer value becomes known. */ + +typedef struct fixup_t +{ + void **pointer; + struct fixup_t *next; +} +fixup_t; + + +/* Structure for holding extra info needed for pointers being read */ + +typedef struct pointer_info +{ + BBT_HEADER (pointer_info); + int integer; + pointer_t type; + + /* The first component of each member of the union is the pointer + being stored */ + + fixup_t *fixup; + + union + { + void *pointer; /* Member for doing pointer searches */ + + struct + { + gfc_symbol *sym; + char true_name[GFC_MAX_SYMBOL_LEN + 1], module[GFC_MAX_SYMBOL_LEN + 1]; + enum + { UNUSED, NEEDED, USED } + state; + int ns, referenced; + module_locus where; + fixup_t *stfixup; + gfc_symtree *symtree; + } + rsym; + + struct + { + gfc_symbol *sym; + enum + { UNREFERENCED = 0, NEEDS_WRITE, WRITTEN } + state; + } + wsym; + } + u; + +} +pointer_info; + +#define gfc_get_pointer_info() gfc_getmem(sizeof(pointer_info)) + + +/* Lists of rename info for the USE statement */ + +typedef struct gfc_use_rename +{ + char local_name[GFC_MAX_SYMBOL_LEN + 1], use_name[GFC_MAX_SYMBOL_LEN + 1]; + struct gfc_use_rename *next; + int found; + gfc_intrinsic_op operator; + locus where; +} +gfc_use_rename; + +#define gfc_get_use_rename() gfc_getmem(sizeof(gfc_use_rename)) + +/* Local variables */ + +/* The FILE for the module we're reading or writing. */ +static FILE *module_fp; + +/* The name of the module we're reading (USE'ing) or writing. */ +static char module_name[GFC_MAX_SYMBOL_LEN + 1]; + +static int module_line, module_column, only_flag; +static enum +{ IO_INPUT, IO_OUTPUT } +iomode; + +static gfc_use_rename *gfc_rename_list; +static pointer_info *pi_root; +static int symbol_number; /* Counter for assigning symbol numbers */ + + + +/*****************************************************************/ + +/* Pointer/integer conversion. Pointers between structures are stored + as integers in the module file. The next couple of subroutines + handle this translation for reading and writing. */ + +/* Recursively free the tree of pointer structures. */ + +static void +free_pi_tree (pointer_info * p) +{ + + if (p == NULL) + return; + + if (p->fixup != NULL) + gfc_internal_error ("free_pi_tree(): Unresolved fixup"); + + free_pi_tree (p->left); + free_pi_tree (p->right); + + gfc_free (p); +} + + +/* Compare pointers when searching by pointer. Used when writing a + module. */ + +static int +compare_pointers (void * _sn1, void * _sn2) +{ + pointer_info *sn1, *sn2; + + sn1 = (pointer_info *) _sn1; + sn2 = (pointer_info *) _sn2; + + if (sn1->u.pointer < sn2->u.pointer) + return -1; + if (sn1->u.pointer > sn2->u.pointer) + return 1; + + return 0; +} + + +/* Compare integers when searching by integer. Used when reading a + module. */ + +static int +compare_integers (void * _sn1, void * _sn2) +{ + pointer_info *sn1, *sn2; + + sn1 = (pointer_info *) _sn1; + sn2 = (pointer_info *) _sn2; + + if (sn1->integer < sn2->integer) + return -1; + if (sn1->integer > sn2->integer) + return 1; + + return 0; +} + + +/* Initialize the pointer_info tree. */ + +static void +init_pi_tree (void) +{ + compare_fn compare; + pointer_info *p; + + pi_root = NULL; + compare = (iomode == IO_INPUT) ? compare_integers : compare_pointers; + + /* Pointer 0 is the NULL pointer. */ + p = gfc_get_pointer_info (); + p->u.pointer = NULL; + p->integer = 0; + p->type = P_OTHER; + + gfc_insert_bbt (&pi_root, p, compare); + + /* Pointer 1 is the current namespace. */ + p = gfc_get_pointer_info (); + p->u.pointer = gfc_current_ns; + p->integer = 1; + p->type = P_NAMESPACE; + + gfc_insert_bbt (&pi_root, p, compare); + + symbol_number = 2; +} + + +/* During module writing, call here with a pointer to something, + returning the pointer_info node. */ + +static pointer_info * +find_pointer (void *gp) +{ + pointer_info *p; + + p = pi_root; + while (p != NULL) + { + if (p->u.pointer == gp) + break; + p = (gp < p->u.pointer) ? p->left : p->right; + } + + return p; +} + + +/* Given a pointer while writing, returns the pointer_info tree node, + creating it if it doesn't exist. */ + +static pointer_info * +get_pointer (void *gp) +{ + pointer_info *p; + + p = find_pointer (gp); + if (p != NULL) + return p; + + /* Pointer doesn't have an integer. Give it one. */ + p = gfc_get_pointer_info (); + + p->u.pointer = gp; + p->integer = symbol_number++; + + gfc_insert_bbt (&pi_root, p, compare_pointers); + + return p; +} + + +/* Given an integer during reading, find it in the pointer_info tree, + creating the node if not found. */ + +static pointer_info * +get_integer (int integer) +{ + pointer_info *p, t; + int c; + + t.integer = integer; + + p = pi_root; + while (p != NULL) + { + c = compare_integers (&t, p); + if (c == 0) + break; + + p = (c < 0) ? p->left : p->right; + } + + if (p != NULL) + return p; + + p = gfc_get_pointer_info (); + p->integer = integer; + p->u.pointer = NULL; + + gfc_insert_bbt (&pi_root, p, compare_integers); + + return p; +} + + +/* Recursive function to find a pointer within a tree by brute force. */ + +static pointer_info * +fp2 (pointer_info * p, const void *target) +{ + pointer_info *q; + + if (p == NULL) + return NULL; + + if (p->u.pointer == target) + return p; + + q = fp2 (p->left, target); + if (q != NULL) + return q; + + return fp2 (p->right, target); +} + + +/* During reading, find a pointer_info node from the pointer value. + This amounts to a brute-force search. */ + +static pointer_info * +find_pointer2 (void *p) +{ + + return fp2 (pi_root, p); +} + + +/* Resolve any fixups using a known pointer. */ +static void +resolve_fixups (fixup_t *f, void * gp) +{ + fixup_t *next; + + for (; f; f = next) + { + next = f->next; + *(f->pointer) = gp; + gfc_free (f); + } +} + +/* Call here during module reading when we know what pointer to + associate with an integer. Any fixups that exist are resolved at + this time. */ + +static void +associate_integer_pointer (pointer_info * p, void *gp) +{ + if (p->u.pointer != NULL) + gfc_internal_error ("associate_integer_pointer(): Already associated"); + + p->u.pointer = gp; + + resolve_fixups (p->fixup, gp); + + p->fixup = NULL; +} + + +/* During module reading, given an integer and a pointer to a pointer, + either store the pointer from an already-known value or create a + fixup structure in order to store things later. Returns zero if + the reference has been actually stored, or nonzero if the reference + must be fixed later (ie associate_integer_pointer must be called + sometime later. Returns the pointer_info structure. */ + +static pointer_info * +add_fixup (int integer, void *gp) +{ + pointer_info *p; + fixup_t *f; + char **cp; + + p = get_integer (integer); + + if (p->integer == 0 || p->u.pointer != NULL) + { + cp = gp; + *cp = p->u.pointer; + } + else + { + f = gfc_getmem (sizeof (fixup_t)); + + f->next = p->fixup; + p->fixup = f; + + f->pointer = gp; + } + + return p; +} + + +/*****************************************************************/ + +/* Parser related subroutines */ + +/* Free the rename list left behind by a USE statement. */ + +static void +free_rename (void) +{ + gfc_use_rename *next; + + for (; gfc_rename_list; gfc_rename_list = next) + { + next = gfc_rename_list->next; + gfc_free (gfc_rename_list); + } +} + + +/* Match a USE statement. */ + +match +gfc_match_use (void) +{ + char name[GFC_MAX_SYMBOL_LEN + 1]; + gfc_use_rename *tail = NULL, *new; + interface_type type; + gfc_intrinsic_op operator; + match m; + + m = gfc_match_name (module_name); + if (m != MATCH_YES) + return m; + + free_rename (); + only_flag = 0; + + if (gfc_match_eos () == MATCH_YES) + return MATCH_YES; + if (gfc_match_char (',') != MATCH_YES) + goto syntax; + + if (gfc_match (" only :") == MATCH_YES) + only_flag = 1; + + if (gfc_match_eos () == MATCH_YES) + return MATCH_YES; + + for (;;) + { + /* Get a new rename struct and add it to the rename list. */ + new = gfc_get_use_rename (); + new->where = *gfc_current_locus (); + new->found = 0; + + if (gfc_rename_list == NULL) + gfc_rename_list = new; + else + tail->next = new; + tail = new; + + /* See what kind of interface we're dealing with. Asusume it is + not an operator. */ + new->operator = INTRINSIC_NONE; + if (gfc_match_generic_spec (&type, name, &operator) == MATCH_ERROR) + goto cleanup; + + switch (type) + { + case INTERFACE_NAMELESS: + gfc_error ("Missing generic specification in USE statement at %C"); + goto cleanup; + + case INTERFACE_GENERIC: + m = gfc_match (" =>"); + + if (only_flag) + { + if (m != MATCH_YES) + strcpy (new->use_name, name); + else + { + strcpy (new->local_name, name); + + m = gfc_match_name (new->use_name); + if (m == MATCH_NO) + goto syntax; + if (m == MATCH_ERROR) + goto cleanup; + } + } + else + { + if (m != MATCH_YES) + goto syntax; + strcpy (new->local_name, name); + + m = gfc_match_name (new->use_name); + if (m == MATCH_NO) + goto syntax; + if (m == MATCH_ERROR) + goto cleanup; + } + + break; + + case INTERFACE_USER_OP: + strcpy (new->use_name, name); + /* Fall through */ + + case INTERFACE_INTRINSIC_OP: + new->operator = operator; + break; + } + + if (gfc_match_eos () == MATCH_YES) + break; + if (gfc_match_char (',') != MATCH_YES) + goto syntax; + } + + return MATCH_YES; + +syntax: + gfc_syntax_error (ST_USE); + +cleanup: + free_rename (); + return MATCH_ERROR; +} + + +/* Given a name, return the name under which to load this symbol. + Returns NULL if this symbol shouldn't be loaded. */ + +static const char * +find_use_name (const char *name) +{ + gfc_use_rename *u; + + for (u = gfc_rename_list; u; u = u->next) + if (strcmp (u->use_name, name) == 0) + break; + + if (u == NULL) + return only_flag ? NULL : name; + + u->found = 1; + + return (u->local_name[0] != '\0') ? u->local_name : name; +} + + +/* Try to find the operator in the current list. */ + +static gfc_use_rename * +find_use_operator (gfc_intrinsic_op operator) +{ + gfc_use_rename *u; + + for (u = gfc_rename_list; u; u = u->next) + if (u->operator == operator) + return u; + + return NULL; +} + + +/*****************************************************************/ + +/* The next couple of subroutines maintain a tree used to avoid a + brute-force search for a combination of true name and module name. + While symtree names, the name that a particular symbol is known by + can changed with USE statements, we still have to keep track of the + true names to generate the correct reference, and also avoid + loading the same real symbol twice in a program unit. + + When we start reading, the true name tree is built and maintained + as symbols are read. The tree is searched as we load new symbols + to see if it already exists someplace in the namespace. */ + +typedef struct true_name +{ + BBT_HEADER (true_name); + gfc_symbol *sym; +} +true_name; + +static true_name *true_name_root; + + +/* Compare two true_name structures. */ + +static int +compare_true_names (void * _t1, void * _t2) +{ + true_name *t1, *t2; + int c; + + t1 = (true_name *) _t1; + t2 = (true_name *) _t2; + + c = strcmp (t1->sym->module, t2->sym->module); + if (c != 0) + return c; + + return strcmp (t1->sym->name, t2->sym->name); +} + + +/* Given a true name, search the true name tree to see if it exists + within the main namespace. */ + +static gfc_symbol * +find_true_name (const char *name, const char *module) +{ + true_name t, *p; + gfc_symbol sym; + int c; + + strcpy (sym.name, name); + strcpy (sym.module, module); + t.sym = &sym; + + p = true_name_root; + while (p != NULL) + { + c = compare_true_names ((void *)(&t), (void *) p); + if (c == 0) + return p->sym; + + p = (c < 0) ? p->left : p->right; + } + + return NULL; +} + + +/* Given a gfc_symbol pointer that is not in the true name tree, add + it. */ + +static void +add_true_name (gfc_symbol * sym) +{ + true_name *t; + + t = gfc_getmem (sizeof (true_name)); + t->sym = sym; + + gfc_insert_bbt (&true_name_root, t, compare_true_names); +} + + +/* Recursive function to build the initial true name tree by + recursively traversing the current namespace. */ + +static void +build_tnt (gfc_symtree * st) +{ + + if (st == NULL) + return; + + build_tnt (st->left); + build_tnt (st->right); + + if (find_true_name (st->n.sym->name, st->n.sym->module) != NULL) + return; + + add_true_name (st->n.sym); +} + + +/* Initialize the true name tree with the current namespace. */ + +static void +init_true_name_tree (void) +{ + true_name_root = NULL; + + build_tnt (gfc_current_ns->sym_root); +} + + +/* Recursively free a true name tree node. */ + +static void +free_true_name (true_name * t) +{ + + if (t == NULL) + return; + free_true_name (t->left); + free_true_name (t->right); + + gfc_free (t); +} + + +/*****************************************************************/ + +/* Module reading and writing. */ + +typedef enum +{ + ATOM_NAME, ATOM_LPAREN, ATOM_RPAREN, ATOM_INTEGER, ATOM_STRING +} +atom_type; + +static atom_type last_atom; + + +/* The name buffer must be at least as long as a symbol name. Right + now it's not clear how we're going to store numeric constants-- + probably as a hexadecimal string, since this will allow the exact + number to be preserved (this can't be done by a decimal + representation). Worry about that later. TODO! */ + +#define MAX_ATOM_SIZE 100 + +static int atom_int; +static char *atom_string, atom_name[MAX_ATOM_SIZE]; + + +/* Report problems with a module. Error reporting is not very + elaborate, since this sorts of errors shouldn't really happen. + This subroutine never returns. */ + +static void bad_module (const char *) ATTRIBUTE_NORETURN; + +static void +bad_module (const char *message) +{ + const char *p; + + switch (iomode) + { + case IO_INPUT: + p = "Reading"; + break; + case IO_OUTPUT: + p = "Writing"; + break; + default: + p = "???"; + break; + } + + fclose (module_fp); + + gfc_fatal_error ("%s module %s at line %d column %d: %s", p, + module_name, module_line, module_column, message); +} + + +/* Set the module's input pointer. */ + +static void +set_module_locus (module_locus * m) +{ + + module_column = m->column; + module_line = m->line; + fsetpos (module_fp, &m->pos); +} + + +/* Get the module's input pointer so that we can restore it later. */ + +static void +get_module_locus (module_locus * m) +{ + + m->column = module_column; + m->line = module_line; + fgetpos (module_fp, &m->pos); +} + + +/* Get the next character in the module, updating our reckoning of + where we are. */ + +static int +module_char (void) +{ + int c; + + c = fgetc (module_fp); + + if (c == EOF) + bad_module ("Unexpected EOF"); + + if (c == '\n') + { + module_line++; + module_column = 0; + } + + module_column++; + return c; +} + + +/* Parse a string constant. The delimiter is guaranteed to be a + single quote. */ + +static void +parse_string (void) +{ + module_locus start; + int len, c; + char *p; + + get_module_locus (&start); + + len = 0; + + /* See how long the string is */ + for ( ; ; ) + { + c = module_char (); + if (c == EOF) + bad_module ("Unexpected end of module in string constant"); + + if (c != '\'') + { + len++; + continue; + } + + c = module_char (); + if (c == '\'') + { + len++; + continue; + } + + break; + } + + set_module_locus (&start); + + atom_string = p = gfc_getmem (len + 1); + + for (; len > 0; len--) + { + c = module_char (); + if (c == '\'') + module_char (); /* Guaranteed to be another \' */ + *p++ = c; + } + + module_char (); /* Terminating \' */ + *p = '\0'; /* C-style string for debug purposes */ +} + + +/* Parse a small integer. */ + +static void +parse_integer (int c) +{ + module_locus m; + + atom_int = c - '0'; + + for (;;) + { + get_module_locus (&m); + + c = module_char (); + if (!ISDIGIT (c)) + break; + + atom_int = 10 * atom_int + c - '0'; + if (atom_int > 99999999) + bad_module ("Integer overflow"); + } + + set_module_locus (&m); +} + + +/* Parse a name. */ + +static void +parse_name (int c) +{ + module_locus m; + char *p; + int len; + + p = atom_name; + + *p++ = c; + len = 1; + + get_module_locus (&m); + + for (;;) + { + c = module_char (); + if (!ISALNUM (c) && c != '_' && c != '-') + break; + + *p++ = c; + if (++len > GFC_MAX_SYMBOL_LEN) + bad_module ("Name too long"); + } + + *p = '\0'; + + fseek (module_fp, -1, SEEK_CUR); + module_column = m.column + len - 1; + + if (c == '\n') + module_line--; +} + + +/* Read the next atom in the module's input stream. */ + +static atom_type +parse_atom (void) +{ + int c; + + do + { + c = module_char (); + } + while (c == ' ' || c == '\n'); + + switch (c) + { + case '(': + return ATOM_LPAREN; + + case ')': + return ATOM_RPAREN; + + case '\'': + parse_string (); + return ATOM_STRING; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + parse_integer (c); + return ATOM_INTEGER; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + parse_name (c); + return ATOM_NAME; + + default: + bad_module ("Bad name"); + } + + /* Not reached */ +} + + +/* Peek at the next atom on the input. */ + +static atom_type +peek_atom (void) +{ + module_locus m; + atom_type a; + + get_module_locus (&m); + + a = parse_atom (); + if (a == ATOM_STRING) + gfc_free (atom_string); + + set_module_locus (&m); + return a; +} + + +/* Read the next atom from the input, requiring that it be a + particular kind. */ + +static void +require_atom (atom_type type) +{ + module_locus m; + atom_type t; + const char *p; + + get_module_locus (&m); + + t = parse_atom (); + if (t != type) + { + switch (type) + { + case ATOM_NAME: + p = "Expected name"; + break; + case ATOM_LPAREN: + p = "Expected left parenthesis"; + break; + case ATOM_RPAREN: + p = "Expected right parenthesis"; + break; + case ATOM_INTEGER: + p = "Expected integer"; + break; + case ATOM_STRING: + p = "Expected string"; + break; + default: + gfc_internal_error ("require_atom(): bad atom type required"); + } + + set_module_locus (&m); + bad_module (p); + } +} + + +/* Given a pointer to an mstring array, require that the current input + be one of the strings in the array. We return the enum value. */ + +static int +find_enum (const mstring * m) +{ + int i; + + i = gfc_string2code (m, atom_name); + if (i >= 0) + return i; + + bad_module ("find_enum(): Enum not found"); + + /* Not reached */ +} + + +/**************** Module output subroutines ***************************/ + +/* Output a character to a module file. */ + +static void +write_char (char out) +{ + + if (fputc (out, module_fp) == EOF) + gfc_fatal_error ("Error writing modules file: %s", strerror (errno)); + + if (out != '\n') + module_column++; + else + { + module_column = 1; + module_line++; + } +} + + +/* Write an atom to a module. The line wrapping isn't perfect, but it + should work most of the time. This isn't that big of a deal, since + the file really isn't meant to be read by people anyway. */ + +static void +write_atom (atom_type atom, const void *v) +{ + char buffer[20]; + int i, len; + const char *p; + + switch (atom) + { + case ATOM_STRING: + case ATOM_NAME: + p = v; + break; + + case ATOM_LPAREN: + p = "("; + break; + + case ATOM_RPAREN: + p = ")"; + break; + + case ATOM_INTEGER: + i = *((const int *) v); + if (i < 0) + gfc_internal_error ("write_atom(): Writing negative integer"); + + sprintf (buffer, "%d", i); + p = buffer; + break; + + default: + gfc_internal_error ("write_atom(): Trying to write dab atom"); + + } + + len = strlen (p); + + if (atom != ATOM_RPAREN) + { + if (module_column + len > 72) + write_char ('\n'); + else + { + + if (last_atom != ATOM_LPAREN && module_column != 1) + write_char (' '); + } + } + + if (atom == ATOM_STRING) + write_char ('\''); + + while (*p) + { + if (atom == ATOM_STRING && *p == '\'') + write_char ('\''); + write_char (*p++); + } + + if (atom == ATOM_STRING) + write_char ('\''); + + last_atom = atom; +} + + + +/***************** Mid-level I/O subroutines *****************/ + +/* These subroutines let their caller read or write atoms without + caring about which of the two is actually happening. This lets a + subroutine concentrate on the actual format of the data being + written. */ + +static void mio_expr (gfc_expr **); +static void mio_symbol_ref (gfc_symbol **); +static void mio_symtree_ref (gfc_symtree **); + +/* Read or write an enumerated value. On writing, we return the input + value for the convenience of callers. We avoid using an integer + pointer because enums are sometimes inside bitfields. */ + +static int +mio_name (int t, const mstring * m) +{ + + if (iomode == IO_OUTPUT) + write_atom (ATOM_NAME, gfc_code2string (m, t)); + else + { + require_atom (ATOM_NAME); + t = find_enum (m); + } + + return t; +} + +/* Specialisation of mio_name. */ + +#define DECL_MIO_NAME(TYPE) \ + static inline TYPE \ + MIO_NAME(TYPE) (TYPE t, const mstring * m) \ + { \ + return (TYPE)mio_name ((int)t, m); \ + } +#define MIO_NAME(TYPE) mio_name_##TYPE + +static void +mio_lparen (void) +{ + + if (iomode == IO_OUTPUT) + write_atom (ATOM_LPAREN, NULL); + else + require_atom (ATOM_LPAREN); +} + + +static void +mio_rparen (void) +{ + + if (iomode == IO_OUTPUT) + write_atom (ATOM_RPAREN, NULL); + else + require_atom (ATOM_RPAREN); +} + + +static void +mio_integer (int *ip) +{ + + if (iomode == IO_OUTPUT) + write_atom (ATOM_INTEGER, ip); + else + { + require_atom (ATOM_INTEGER); + *ip = atom_int; + } +} + + +/* Read or write a character pointer that points to a string on the + heap. */ + +static void +mio_allocated_string (char **sp) +{ + + if (iomode == IO_OUTPUT) + write_atom (ATOM_STRING, *sp); + else + { + require_atom (ATOM_STRING); + *sp = atom_string; + } +} + + +/* Read or write a string that is in static memory or inside of some + already-allocated structure. */ + +static void +mio_internal_string (char *string) +{ + + if (iomode == IO_OUTPUT) + write_atom (ATOM_STRING, string); + else + { + require_atom (ATOM_STRING); + strcpy (string, atom_string); + gfc_free (atom_string); + } +} + + + +typedef enum +{ AB_ALLOCATABLE, AB_DIMENSION, AB_EXTERNAL, AB_INTRINSIC, AB_OPTIONAL, + AB_POINTER, AB_SAVE, AB_TARGET, AB_DUMMY, AB_COMMON, AB_RESULT, + AB_ENTRY, AB_DATA, AB_IN_NAMELIST, AB_IN_COMMON, AB_SAVED_COMMON, + AB_FUNCTION, AB_SUBROUTINE, AB_SEQUENCE, AB_ELEMENTAL, AB_PURE, + AB_RECURSIVE, AB_GENERIC, AB_ALWAYS_EXPLICIT +} +ab_attribute; + +static const mstring attr_bits[] = +{ + minit ("ALLOCATABLE", AB_ALLOCATABLE), + minit ("DIMENSION", AB_DIMENSION), + minit ("EXTERNAL", AB_EXTERNAL), + minit ("INTRINSIC", AB_INTRINSIC), + minit ("OPTIONAL", AB_OPTIONAL), + minit ("POINTER", AB_POINTER), + minit ("SAVE", AB_SAVE), + minit ("TARGET", AB_TARGET), + minit ("DUMMY", AB_DUMMY), + minit ("COMMON", AB_COMMON), + minit ("RESULT", AB_RESULT), + minit ("ENTRY", AB_ENTRY), + minit ("DATA", AB_DATA), + minit ("IN_NAMELIST", AB_IN_NAMELIST), + minit ("IN_COMMON", AB_IN_COMMON), + minit ("SAVED_COMMON", AB_SAVED_COMMON), + minit ("FUNCTION", AB_FUNCTION), + minit ("SUBROUTINE", AB_SUBROUTINE), + minit ("SEQUENCE", AB_SEQUENCE), + minit ("ELEMENTAL", AB_ELEMENTAL), + minit ("PURE", AB_PURE), + minit ("RECURSIVE", AB_RECURSIVE), + minit ("GENERIC", AB_GENERIC), + minit ("ALWAYS_EXPLICIT", AB_ALWAYS_EXPLICIT), + minit (NULL, -1) +}; + +/* Specialisation of mio_name. */ +DECL_MIO_NAME(ab_attribute) +DECL_MIO_NAME(ar_type) +DECL_MIO_NAME(array_type) +DECL_MIO_NAME(bt) +DECL_MIO_NAME(expr_t) +DECL_MIO_NAME(gfc_access) +DECL_MIO_NAME(gfc_intrinsic_op) +DECL_MIO_NAME(ifsrc) +DECL_MIO_NAME(procedure_type) +DECL_MIO_NAME(ref_type) +DECL_MIO_NAME(sym_flavor) +DECL_MIO_NAME(sym_intent) +#undef DECL_MIO_NAME + +/* Symbol attributes are stored in list with the first three elements + being the enumerated fields, while the remaining elements (if any) + indicate the individual attribute bits. The access field is not + saved-- it controls what symbols are exported when a module is + written. */ + +static void +mio_symbol_attribute (symbol_attribute * attr) +{ + atom_type t; + + mio_lparen (); + + attr->flavor = MIO_NAME(sym_flavor) (attr->flavor, flavors); + attr->intent = MIO_NAME(sym_intent) (attr->intent, intents); + attr->proc = MIO_NAME(procedure_type) (attr->proc, procedures); + attr->if_source = MIO_NAME(ifsrc) (attr->if_source, ifsrc_types); + + if (iomode == IO_OUTPUT) + { + if (attr->allocatable) + MIO_NAME(ab_attribute) (AB_ALLOCATABLE, attr_bits); + if (attr->dimension) + MIO_NAME(ab_attribute) (AB_DIMENSION, attr_bits); + if (attr->external) + MIO_NAME(ab_attribute) (AB_EXTERNAL, attr_bits); + if (attr->intrinsic) + MIO_NAME(ab_attribute) (AB_INTRINSIC, attr_bits); + if (attr->optional) + MIO_NAME(ab_attribute) (AB_OPTIONAL, attr_bits); + if (attr->pointer) + MIO_NAME(ab_attribute) (AB_POINTER, attr_bits); + if (attr->save) + MIO_NAME(ab_attribute) (AB_SAVE, attr_bits); + if (attr->target) + MIO_NAME(ab_attribute) (AB_TARGET, attr_bits); + if (attr->dummy) + MIO_NAME(ab_attribute) (AB_DUMMY, attr_bits); + if (attr->common) + MIO_NAME(ab_attribute) (AB_COMMON, attr_bits); + if (attr->result) + MIO_NAME(ab_attribute) (AB_RESULT, attr_bits); + if (attr->entry) + MIO_NAME(ab_attribute) (AB_ENTRY, attr_bits); + + if (attr->data) + MIO_NAME(ab_attribute) (AB_DATA, attr_bits); + if (attr->in_namelist) + MIO_NAME(ab_attribute) (AB_IN_NAMELIST, attr_bits); + if (attr->in_common) + MIO_NAME(ab_attribute) (AB_IN_COMMON, attr_bits); + if (attr->saved_common) + MIO_NAME(ab_attribute) (AB_SAVED_COMMON, attr_bits); + + if (attr->function) + MIO_NAME(ab_attribute) (AB_FUNCTION, attr_bits); + if (attr->subroutine) + MIO_NAME(ab_attribute) (AB_SUBROUTINE, attr_bits); + if (attr->generic) + MIO_NAME(ab_attribute) (AB_GENERIC, attr_bits); + + if (attr->sequence) + MIO_NAME(ab_attribute) (AB_SEQUENCE, attr_bits); + if (attr->elemental) + MIO_NAME(ab_attribute) (AB_ELEMENTAL, attr_bits); + if (attr->pure) + MIO_NAME(ab_attribute) (AB_PURE, attr_bits); + if (attr->recursive) + MIO_NAME(ab_attribute) (AB_RECURSIVE, attr_bits); + if (attr->always_explicit) + MIO_NAME(ab_attribute) (AB_ALWAYS_EXPLICIT, attr_bits); + + mio_rparen (); + + } + else + { + + for (;;) + { + t = parse_atom (); + if (t == ATOM_RPAREN) + break; + if (t != ATOM_NAME) + bad_module ("Expected attribute bit name"); + + switch ((ab_attribute) find_enum (attr_bits)) + { + case AB_ALLOCATABLE: + attr->allocatable = 1; + break; + case AB_DIMENSION: + attr->dimension = 1; + break; + case AB_EXTERNAL: + attr->external = 1; + break; + case AB_INTRINSIC: + attr->intrinsic = 1; + break; + case AB_OPTIONAL: + attr->optional = 1; + break; + case AB_POINTER: + attr->pointer = 1; + break; + case AB_SAVE: + attr->save = 1; + break; + case AB_TARGET: + attr->target = 1; + break; + case AB_DUMMY: + attr->dummy = 1; + break; + case AB_COMMON: + attr->common = 1; + break; + case AB_RESULT: + attr->result = 1; + break; + case AB_ENTRY: + attr->entry = 1; + break; + case AB_DATA: + attr->data = 1; + break; + case AB_IN_NAMELIST: + attr->in_namelist = 1; + break; + case AB_IN_COMMON: + attr->in_common = 1; + break; + case AB_SAVED_COMMON: + attr->saved_common = 1; + break; + case AB_FUNCTION: + attr->function = 1; + break; + case AB_SUBROUTINE: + attr->subroutine = 1; + break; + case AB_GENERIC: + attr->generic = 1; + break; + case AB_SEQUENCE: + attr->sequence = 1; + break; + case AB_ELEMENTAL: + attr->elemental = 1; + break; + case AB_PURE: + attr->pure = 1; + break; + case AB_RECURSIVE: + attr->recursive = 1; + break; + case AB_ALWAYS_EXPLICIT: + attr->always_explicit = 1; + break; + } + } + } +} + + +static const mstring bt_types[] = { + minit ("INTEGER", BT_INTEGER), + minit ("REAL", BT_REAL), + minit ("COMPLEX", BT_COMPLEX), + minit ("LOGICAL", BT_LOGICAL), + minit ("CHARACTER", BT_CHARACTER), + minit ("DERIVED", BT_DERIVED), + minit ("PROCEDURE", BT_PROCEDURE), + minit ("UNKNOWN", BT_UNKNOWN), + minit (NULL, -1) +}; + + +static void +mio_charlen (gfc_charlen ** clp) +{ + gfc_charlen *cl; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + cl = *clp; + if (cl != NULL) + mio_expr (&cl->length); + } + else + { + + if (peek_atom () != ATOM_RPAREN) + { + cl = gfc_get_charlen (); + mio_expr (&cl->length); + + *clp = cl; + + cl->next = gfc_current_ns->cl_list; + gfc_current_ns->cl_list = cl; + } + } + + mio_rparen (); +} + + +/* Return a symtree node with a name that is guaranteed to be unique + within the namespace and corresponds to an illegal fortran name. */ + +static gfc_symtree * +get_unique_symtree (gfc_namespace * ns) +{ + char name[GFC_MAX_SYMBOL_LEN + 1]; + static int serial = 0; + + sprintf (name, "@%d", serial++); + return gfc_new_symtree (&ns->sym_root, name); +} + + +/* See if a name is a generated name. */ + +static int +check_unique_name (const char *name) +{ + + return *name == '@'; +} + + +static void +mio_typespec (gfc_typespec * ts) +{ + + mio_lparen (); + + ts->type = MIO_NAME(bt) (ts->type, bt_types); + + if (ts->type != BT_DERIVED) + mio_integer (&ts->kind); + else + mio_symbol_ref (&ts->derived); + + mio_charlen (&ts->cl); + + mio_rparen (); +} + + +static const mstring array_spec_types[] = { + minit ("EXPLICIT", AS_EXPLICIT), + minit ("ASSUMED_SHAPE", AS_ASSUMED_SHAPE), + minit ("DEFERRED", AS_DEFERRED), + minit ("ASSUMED_SIZE", AS_ASSUMED_SIZE), + minit (NULL, -1) +}; + + +static void +mio_array_spec (gfc_array_spec ** asp) +{ + gfc_array_spec *as; + int i; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + if (*asp == NULL) + goto done; + as = *asp; + } + else + { + if (peek_atom () == ATOM_RPAREN) + { + *asp = NULL; + goto done; + } + + *asp = as = gfc_get_array_spec (); + } + + mio_integer (&as->rank); + as->type = MIO_NAME(array_type) (as->type, array_spec_types); + + for (i = 0; i < as->rank; i++) + { + mio_expr (&as->lower[i]); + mio_expr (&as->upper[i]); + } + +done: + mio_rparen (); +} + + +/* Given a pointer to an array reference structure (which lives in a + gfc_ref structure), find the corresponding array specification + structure. Storing the pointer in the ref structure doesn't quite + work when loading from a module. Generating code for an array + reference also needs more infomation than just the array spec. */ + +static const mstring array_ref_types[] = { + minit ("FULL", AR_FULL), + minit ("ELEMENT", AR_ELEMENT), + minit ("SECTION", AR_SECTION), + minit (NULL, -1) +}; + +static void +mio_array_ref (gfc_array_ref * ar) +{ + int i; + + mio_lparen (); + ar->type = MIO_NAME(ar_type) (ar->type, array_ref_types); + mio_integer (&ar->dimen); + + switch (ar->type) + { + case AR_FULL: + break; + + case AR_ELEMENT: + for (i = 0; i < ar->dimen; i++) + mio_expr (&ar->start[i]); + + break; + + case AR_SECTION: + for (i = 0; i < ar->dimen; i++) + { + mio_expr (&ar->start[i]); + mio_expr (&ar->end[i]); + mio_expr (&ar->stride[i]); + } + + break; + + case AR_UNKNOWN: + gfc_internal_error ("mio_array_ref(): Unknown array ref"); + } + + for (i = 0; i < ar->dimen; i++) + mio_integer ((int *) &ar->dimen_type[i]); + + if (iomode == IO_INPUT) + { + ar->where = *gfc_current_locus (); + + for (i = 0; i < ar->dimen; i++) + ar->c_where[i] = *gfc_current_locus (); + } + + mio_rparen (); +} + + +/* Saves or restores a pointer. The pointer is converted back and + forth from an integer. We return the pointer_info pointer so that + the caller can take additional action based on the pointer type. */ + +static pointer_info * +mio_pointer_ref (void *gp) +{ + pointer_info *p; + + if (iomode == IO_OUTPUT) + { + p = get_pointer (*((char **) gp)); + write_atom (ATOM_INTEGER, &p->integer); + } + else + { + require_atom (ATOM_INTEGER); + p = add_fixup (atom_int, gp); + } + + return p; +} + + +/* Save and load references to components that occur within + expressions. We have to describe these references by a number and + by name. The number is necessary for forward references during + reading, and the name is necessary if the symbol already exists in + the namespace and is not loaded again. */ + +static void +mio_component_ref (gfc_component ** cp, gfc_symbol * sym) +{ + char name[GFC_MAX_SYMBOL_LEN + 1]; + gfc_component *q; + pointer_info *p; + + p = mio_pointer_ref (cp); + if (p->type == P_UNKNOWN) + p->type = P_COMPONENT; + + if (iomode == IO_OUTPUT) + mio_internal_string ((*cp)->name); + else + { + mio_internal_string (name); + + if (sym->components != NULL && p->u.pointer == NULL) + { + /* Symbol already loaded, so search by name. */ + for (q = sym->components; q; q = q->next) + if (strcmp (q->name, name) == 0) + break; + + if (q == NULL) + gfc_internal_error ("mio_component_ref(): Component not found"); + + associate_integer_pointer (p, q); + } + + /* Make sure this symbol will eventually be loaded. */ + p = find_pointer2 (sym); + if (p->u.rsym.state == UNUSED) + p->u.rsym.state = NEEDED; + } +} + + +static void +mio_component (gfc_component * c) +{ + pointer_info *p; + int n; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + p = get_pointer (c); + mio_integer (&p->integer); + } + else + { + mio_integer (&n); + p = get_integer (n); + associate_integer_pointer (p, c); + } + + if (p->type == P_UNKNOWN) + p->type = P_COMPONENT; + + mio_internal_string (c->name); + mio_typespec (&c->ts); + mio_array_spec (&c->as); + + mio_integer (&c->dimension); + mio_integer (&c->pointer); + + mio_expr (&c->initializer); + mio_rparen (); +} + + +static void +mio_component_list (gfc_component ** cp) +{ + gfc_component *c, *tail; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + for (c = *cp; c; c = c->next) + mio_component (c); + } + else + { + + *cp = NULL; + tail = NULL; + + for (;;) + { + if (peek_atom () == ATOM_RPAREN) + break; + + c = gfc_get_component (); + mio_component (c); + + if (tail == NULL) + *cp = c; + else + tail->next = c; + + tail = c; + } + } + + mio_rparen (); +} + + +static void +mio_actual_arg (gfc_actual_arglist * a) +{ + + mio_lparen (); + mio_internal_string (a->name); + mio_expr (&a->expr); + mio_rparen (); +} + + +static void +mio_actual_arglist (gfc_actual_arglist ** ap) +{ + gfc_actual_arglist *a, *tail; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + for (a = *ap; a; a = a->next) + mio_actual_arg (a); + + } + else + { + tail = NULL; + + for (;;) + { + if (peek_atom () != ATOM_LPAREN) + break; + + a = gfc_get_actual_arglist (); + + if (tail == NULL) + *ap = a; + else + tail->next = a; + + tail = a; + mio_actual_arg (a); + } + } + + mio_rparen (); +} + + +/* Read and write formal argument lists. */ + +static void +mio_formal_arglist (gfc_symbol * sym) +{ + gfc_formal_arglist *f, *tail; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + for (f = sym->formal; f; f = f->next) + mio_symbol_ref (&f->sym); + + } + else + { + sym->formal = tail = NULL; + + while (peek_atom () != ATOM_RPAREN) + { + f = gfc_get_formal_arglist (); + mio_symbol_ref (&f->sym); + + if (sym->formal == NULL) + sym->formal = f; + else + tail->next = f; + + tail = f; + } + } + + mio_rparen (); +} + + +/* Save or restore a reference to a symbol node. */ + +void +mio_symbol_ref (gfc_symbol ** symp) +{ + pointer_info *p; + + p = mio_pointer_ref (symp); + if (p->type == P_UNKNOWN) + p->type = P_SYMBOL; + + if (iomode == IO_OUTPUT) + { + if (p->u.wsym.state == UNREFERENCED) + p->u.wsym.state = NEEDS_WRITE; + } + else + { + if (p->u.rsym.state == UNUSED) + p->u.rsym.state = NEEDED; + } +} + + +/* Save or restore a reference to a symtree node. */ + +static void +mio_symtree_ref (gfc_symtree ** stp) +{ + pointer_info *p; + fixup_t *f; + + if (iomode == IO_OUTPUT) + { + mio_symbol_ref (&(*stp)->n.sym); + } + else + { + require_atom (ATOM_INTEGER); + p = get_integer (atom_int); + if (p->type == P_UNKNOWN) + p->type = P_SYMBOL; + + if (p->u.rsym.state == UNUSED) + p->u.rsym.state = NEEDED; + + if (p->u.rsym.symtree != NULL) + { + *stp = p->u.rsym.symtree; + } + else + { + f = gfc_getmem (sizeof (fixup_t)); + + f->next = p->u.rsym.stfixup; + p->u.rsym.stfixup = f; + + f->pointer = (void **)stp; + } + } +} + +static void +mio_iterator (gfc_iterator ** ip) +{ + gfc_iterator *iter; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + if (*ip == NULL) + goto done; + } + else + { + if (peek_atom () == ATOM_RPAREN) + { + *ip = NULL; + goto done; + } + + *ip = gfc_get_iterator (); + } + + iter = *ip; + + mio_expr (&iter->var); + mio_expr (&iter->start); + mio_expr (&iter->end); + mio_expr (&iter->step); + +done: + mio_rparen (); +} + + + +static void +mio_constructor (gfc_constructor ** cp) +{ + gfc_constructor *c, *tail; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + for (c = *cp; c; c = c->next) + { + mio_lparen (); + mio_expr (&c->expr); + mio_iterator (&c->iterator); + mio_rparen (); + } + } + else + { + + *cp = NULL; + tail = NULL; + + while (peek_atom () != ATOM_RPAREN) + { + c = gfc_get_constructor (); + + if (tail == NULL) + *cp = c; + else + tail->next = c; + + tail = c; + + mio_lparen (); + mio_expr (&c->expr); + mio_iterator (&c->iterator); + mio_rparen (); + } + } + + mio_rparen (); +} + + + +static const mstring ref_types[] = { + minit ("ARRAY", REF_ARRAY), + minit ("COMPONENT", REF_COMPONENT), + minit ("SUBSTRING", REF_SUBSTRING), + minit (NULL, -1) +}; + + +static void +mio_ref (gfc_ref ** rp) +{ + gfc_ref *r; + + mio_lparen (); + + r = *rp; + r->type = MIO_NAME(ref_type) (r->type, ref_types); + + switch (r->type) + { + case REF_ARRAY: + mio_array_ref (&r->u.ar); + break; + + case REF_COMPONENT: + mio_symbol_ref (&r->u.c.sym); + mio_component_ref (&r->u.c.component, r->u.c.sym); + break; + + case REF_SUBSTRING: + mio_expr (&r->u.ss.start); + mio_expr (&r->u.ss.end); + mio_charlen (&r->u.ss.length); + break; + } + + mio_rparen (); +} + + +static void +mio_ref_list (gfc_ref ** rp) +{ + gfc_ref *ref, *head, *tail; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + for (ref = *rp; ref; ref = ref->next) + mio_ref (&ref); + } + else + { + head = tail = NULL; + + while (peek_atom () != ATOM_RPAREN) + { + if (head == NULL) + head = tail = gfc_get_ref (); + else + { + tail->next = gfc_get_ref (); + tail = tail->next; + } + + mio_ref (&tail); + } + + *rp = head; + } + + mio_rparen (); +} + + +/* Read and write an integer value. */ + +static void +mio_gmp_integer (mpz_t * integer) +{ + char *p; + + if (iomode == IO_INPUT) + { + if (parse_atom () != ATOM_STRING) + bad_module ("Expected integer string"); + + mpz_init (*integer); + if (mpz_set_str (*integer, atom_string, 10)) + bad_module ("Error converting integer"); + + gfc_free (atom_string); + + } + else + { + p = mpz_get_str (NULL, 10, *integer); + write_atom (ATOM_STRING, p); + gfc_free (p); + } +} + + +static void +mio_gmp_real (mpf_t * real) +{ + mp_exp_t exponent; + char *p; + + if (iomode == IO_INPUT) + { + if (parse_atom () != ATOM_STRING) + bad_module ("Expected real string"); + + mpf_init (*real); + mpf_set_str (*real, atom_string, -16); + gfc_free (atom_string); + + } + else + { + p = mpf_get_str (NULL, &exponent, 16, 0, *real); + atom_string = gfc_getmem (strlen (p) + 20); + + sprintf (atom_string, "0.%s@%ld", p, exponent); + write_atom (ATOM_STRING, atom_string); + + gfc_free (atom_string); + gfc_free (p); + } +} + + +/* Save and restore the shape of an array constructor. */ + +static void +mio_shape (mpz_t ** pshape, int rank) +{ + mpz_t *shape; + atom_type t; + int n; + + /* A NULL shape is represented by (). */ + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + shape = *pshape; + if (!shape) + { + mio_rparen (); + return; + } + } + else + { + t = peek_atom (); + if (t == ATOM_RPAREN) + { + *pshape = NULL; + mio_rparen (); + return; + } + + shape = gfc_get_shape (rank); + *pshape = shape; + } + + for (n = 0; n < rank; n++) + mio_gmp_integer (&shape[n]); + + mio_rparen (); +} + + +static const mstring expr_types[] = { + minit ("OP", EXPR_OP), + minit ("FUNCTION", EXPR_FUNCTION), + minit ("CONSTANT", EXPR_CONSTANT), + minit ("VARIABLE", EXPR_VARIABLE), + minit ("SUBSTRING", EXPR_SUBSTRING), + minit ("STRUCTURE", EXPR_STRUCTURE), + minit ("ARRAY", EXPR_ARRAY), + minit ("NULL", EXPR_NULL), + minit (NULL, -1) +}; + +/* INTRINSIC_ASSIGN is missing because it is used as an index for + generic operators, not in expressions. INTRINSIC_USER is also + replaced by the correct function name by the time we see it. */ + +static const mstring intrinsics[] = +{ + minit ("UPLUS", INTRINSIC_UPLUS), + minit ("UMINUS", INTRINSIC_UMINUS), + minit ("PLUS", INTRINSIC_PLUS), + minit ("MINUS", INTRINSIC_MINUS), + minit ("TIMES", INTRINSIC_TIMES), + minit ("DIVIDE", INTRINSIC_DIVIDE), + minit ("POWER", INTRINSIC_POWER), + minit ("CONCAT", INTRINSIC_CONCAT), + minit ("AND", INTRINSIC_AND), + minit ("OR", INTRINSIC_OR), + minit ("EQV", INTRINSIC_EQV), + minit ("NEQV", INTRINSIC_NEQV), + minit ("EQ", INTRINSIC_EQ), + minit ("NE", INTRINSIC_NE), + minit ("GT", INTRINSIC_GT), + minit ("GE", INTRINSIC_GE), + minit ("LT", INTRINSIC_LT), + minit ("LE", INTRINSIC_LE), + minit ("NOT", INTRINSIC_NOT), + minit (NULL, -1) +}; + +/* Read and write expressions. The form "()" is allowed to indicate a + NULL expression. */ + +static void +mio_expr (gfc_expr ** ep) +{ + gfc_expr *e; + atom_type t; + int flag; + + mio_lparen (); + + if (iomode == IO_OUTPUT) + { + if (*ep == NULL) + { + mio_rparen (); + return; + } + + e = *ep; + MIO_NAME(expr_t) (e->expr_type, expr_types); + + } + else + { + t = parse_atom (); + if (t == ATOM_RPAREN) + { + *ep = NULL; + return; + } + + if (t != ATOM_NAME) + bad_module ("Expected expression type"); + + e = *ep = gfc_get_expr (); + e->where = *gfc_current_locus (); + e->expr_type = (expr_t) find_enum (expr_types); + } + + mio_typespec (&e->ts); + mio_integer (&e->rank); + + switch (e->expr_type) + { + case EXPR_OP: + e->operator = MIO_NAME(gfc_intrinsic_op) (e->operator, intrinsics); + + switch (e->operator) + { + case INTRINSIC_UPLUS: + case INTRINSIC_UMINUS: + case INTRINSIC_NOT: + mio_expr (&e->op1); + break; + + case INTRINSIC_PLUS: + case INTRINSIC_MINUS: + case INTRINSIC_TIMES: + case INTRINSIC_DIVIDE: + case INTRINSIC_POWER: + case INTRINSIC_CONCAT: + case INTRINSIC_AND: + case INTRINSIC_OR: + case INTRINSIC_EQV: + case INTRINSIC_NEQV: + case INTRINSIC_EQ: + case INTRINSIC_NE: + case INTRINSIC_GT: + case INTRINSIC_GE: + case INTRINSIC_LT: + case INTRINSIC_LE: + mio_expr (&e->op1); + mio_expr (&e->op2); + break; + + default: + bad_module ("Bad operator"); + } + + break; + + case EXPR_FUNCTION: + mio_symtree_ref (&e->symtree); + mio_actual_arglist (&e->value.function.actual); + + if (iomode == IO_OUTPUT) + { + mio_allocated_string (&e->value.function.name); + flag = e->value.function.esym != NULL; + mio_integer (&flag); + if (flag) + mio_symbol_ref (&e->value.function.esym); + else + write_atom (ATOM_STRING, e->value.function.isym->name); + + } + else + { + require_atom (ATOM_STRING); + e->value.function.name = gfc_get_string (atom_string); + gfc_free (atom_string); + + mio_integer (&flag); + if (flag) + mio_symbol_ref (&e->value.function.esym); + else + { + require_atom (ATOM_STRING); + e->value.function.isym = gfc_find_function (atom_string); + gfc_free (atom_string); + } + } + + break; + + case EXPR_VARIABLE: + mio_symtree_ref (&e->symtree); + mio_ref_list (&e->ref); + break; + + case EXPR_SUBSTRING: + mio_allocated_string (&e->value.character.string); + mio_expr (&e->op1); + mio_expr (&e->op2); + break; + + case EXPR_STRUCTURE: + case EXPR_ARRAY: + mio_constructor (&e->value.constructor); + mio_shape (&e->shape, e->rank); + break; + + case EXPR_CONSTANT: + switch (e->ts.type) + { + case BT_INTEGER: + mio_gmp_integer (&e->value.integer); + break; + + case BT_REAL: + mio_gmp_real (&e->value.real); + break; + + case BT_COMPLEX: + mio_gmp_real (&e->value.complex.r); + mio_gmp_real (&e->value.complex.i); + break; + + case BT_LOGICAL: + mio_integer (&e->value.logical); + break; + + case BT_CHARACTER: + mio_integer (&e->value.character.length); + mio_allocated_string (&e->value.character.string); + break; + + default: + bad_module ("Bad type in constant expression"); + } + + break; + + case EXPR_NULL: + break; + } + + mio_rparen (); +} + + +/* Save/restore lists of gfc_interface stuctures. When loading an + interface, we are really appending to the existing list of + interfaces. Checking for duplicate and ambiguous interfaces has to + be done later when all symbols have been loaded. */ + +static void +mio_interface_rest (gfc_interface ** ip) +{ + gfc_interface *tail, *p; + + if (iomode == IO_OUTPUT) + { + if (ip != NULL) + for (p = *ip; p; p = p->next) + mio_symbol_ref (&p->sym); + } + else + { + + if (*ip == NULL) + tail = NULL; + else + { + tail = *ip; + while (tail->next) + tail = tail->next; + } + + for (;;) + { + if (peek_atom () == ATOM_RPAREN) + break; + + p = gfc_get_interface (); + mio_symbol_ref (&p->sym); + + if (tail == NULL) + *ip = p; + else + tail->next = p; + + tail = p; + } + } + + mio_rparen (); +} + + +/* Save/restore a nameless operator interface. */ + +static void +mio_interface (gfc_interface ** ip) +{ + + mio_lparen (); + mio_interface_rest (ip); +} + + +/* Save/restore a named operator interface. */ + +static void +mio_symbol_interface (char *name, char *module, + gfc_interface ** ip) +{ + + mio_lparen (); + + mio_internal_string (name); + mio_internal_string (module); + + mio_interface_rest (ip); +} + + +static void +mio_namespace_ref (gfc_namespace ** nsp) +{ + gfc_namespace *ns; + pointer_info *p; + + p = mio_pointer_ref (nsp); + + if (p->type == P_UNKNOWN) + p->type = P_NAMESPACE; + + if (iomode == IO_INPUT && p->integer != 0 && p->u.pointer == NULL) + { + ns = gfc_get_namespace (NULL); + associate_integer_pointer (p, ns); + } +} + + +/* Unlike most other routines, the address of the symbol node is + already fixed on input and the name/module has already been filled + in. */ + +static void +mio_symbol (gfc_symbol * sym) +{ + gfc_formal_arglist *formal; + + mio_lparen (); + + mio_symbol_attribute (&sym->attr); + mio_typespec (&sym->ts); + + /* Contained procedures don't have formal namespaces. Instead we output the + procedure namespace. The will contain the formal arguments. */ + if (iomode == IO_OUTPUT) + { + formal = sym->formal; + while (formal && !formal->sym) + formal = formal->next; + + if (formal) + mio_namespace_ref (&formal->sym->ns); + else + mio_namespace_ref (&sym->formal_ns); + } + else + { + mio_namespace_ref (&sym->formal_ns); + if (sym->formal_ns) + { + sym->formal_ns->proc_name = sym; + sym->refs++; + } + } + + /* Save/restore common block links */ + mio_symbol_ref (&sym->common_head); + mio_symbol_ref (&sym->common_next); + + mio_formal_arglist (sym); + + mio_expr (&sym->value); + mio_array_spec (&sym->as); + + mio_symbol_ref (&sym->result); + + /* Note that components are always saved, even if they are supposed + to be private. Component access is checked during searching. */ + + mio_component_list (&sym->components); + + if (sym->components != NULL) + sym->component_access = + MIO_NAME(gfc_access) (sym->component_access, access_types); + + mio_symbol_ref (&sym->common_head); + mio_symbol_ref (&sym->common_next); + + mio_rparen (); +} + + +/************************* Top level subroutines *************************/ + +/* Skip a list between balanced left and right parens. */ + +static void +skip_list (void) +{ + int level; + + level = 0; + do + { + switch (parse_atom ()) + { + case ATOM_LPAREN: + level++; + break; + + case ATOM_RPAREN: + level--; + break; + + case ATOM_STRING: + gfc_free (atom_string); + break; + + case ATOM_NAME: + case ATOM_INTEGER: + break; + } + } + while (level > 0); +} + + +/* Load operator interfaces from the module. Interfaces are unusual + in that they attach themselves to existing symbols. */ + +static void +load_operator_interfaces (void) +{ + const char *p; + char name[GFC_MAX_SYMBOL_LEN + 1], module[GFC_MAX_SYMBOL_LEN + 1]; + gfc_user_op *uop; + + mio_lparen (); + + while (peek_atom () != ATOM_RPAREN) + { + mio_lparen (); + + mio_internal_string (name); + mio_internal_string (module); + + /* Decide if we need to load this one or not. */ + p = find_use_name (name); + if (p == NULL) + { + while (parse_atom () != ATOM_RPAREN); + } + else + { + uop = gfc_get_uop (p); + mio_interface_rest (&uop->operator); + } + } + + mio_rparen (); +} + + +/* Load interfaces from the module. Interfaces are unusual in that + they attach themselves to existing symbols. */ + +static void +load_generic_interfaces (void) +{ + const char *p; + char name[GFC_MAX_SYMBOL_LEN + 1], module[GFC_MAX_SYMBOL_LEN + 1]; + gfc_symbol *sym; + + mio_lparen (); + + while (peek_atom () != ATOM_RPAREN) + { + mio_lparen (); + + mio_internal_string (name); + mio_internal_string (module); + + /* Decide if we need to load this one or not. */ + p = find_use_name (name); + + if (p == NULL || gfc_find_symbol (p, NULL, 0, &sym)) + { + while (parse_atom () != ATOM_RPAREN); + continue; + } + + if (sym == NULL) + { + gfc_get_symbol (p, NULL, &sym); + + sym->attr.flavor = FL_PROCEDURE; + sym->attr.generic = 1; + sym->attr.use_assoc = 1; + } + + mio_interface_rest (&sym->generic); + } + + mio_rparen (); +} + + +/* Recursive function to traverse the pointer_info tree and load a + needed symbol. We return nonzero if we load a symbol and stop the + traversal, because the act of loading can alter the tree. */ + +static int +load_needed (pointer_info * p) +{ + gfc_namespace *ns; + pointer_info *q; + gfc_symbol *sym; + + if (p == NULL) + return 0; + if (load_needed (p->left)) + return 1; + if (load_needed (p->right)) + return 1; + + if (p->type != P_SYMBOL || p->u.rsym.state != NEEDED) + return 0; + + p->u.rsym.state = USED; + + set_module_locus (&p->u.rsym.where); + + sym = p->u.rsym.sym; + if (sym == NULL) + { + q = get_integer (p->u.rsym.ns); + + ns = (gfc_namespace *) q->u.pointer; + if (ns == NULL) + { + /* Create an interface namespace if necessary. These are + the namespaces that hold the formal parameters of module + procedures. */ + + ns = gfc_get_namespace (NULL); + associate_integer_pointer (q, ns); + } + + sym = gfc_new_symbol (p->u.rsym.true_name, ns); + strcpy (sym->module, p->u.rsym.module); + + associate_integer_pointer (p, sym); + } + + mio_symbol (sym); + sym->attr.use_assoc = 1; + + return 1; +} + + +/* Recursive function for cleaning up things after a module has been + read. */ + +static void +read_cleanup (pointer_info * p) +{ + gfc_symtree *st; + pointer_info *q; + + if (p == NULL) + return; + + read_cleanup (p->left); + read_cleanup (p->right); + + if (p->type == P_SYMBOL && p->u.rsym.state == USED && !p->u.rsym.referenced) + { + /* Add hidden symbols to the symtree. */ + q = get_integer (p->u.rsym.ns); + st = get_unique_symtree ((gfc_namespace *) q->u.pointer); + + st->n.sym = p->u.rsym.sym; + st->n.sym->refs++; + + /* Fixup any symtree references. */ + p->u.rsym.symtree = st; + resolve_fixups (p->u.rsym.stfixup, st); + p->u.rsym.stfixup = NULL; + } + + /* Free unused symbols. */ + if (p->type == P_SYMBOL && p->u.rsym.state == UNUSED) + gfc_free_symbol (p->u.rsym.sym); +} + + +/* Read a module file. */ + +static void +read_module (void) +{ + module_locus operator_interfaces, user_operators; + const char *p; + char name[GFC_MAX_SYMBOL_LEN + 1]; + gfc_intrinsic_op i; + int ambiguous, symbol; + pointer_info *info; + gfc_use_rename *u; + gfc_symtree *st; + gfc_symbol *sym; + + get_module_locus (&operator_interfaces); /* Skip these for now */ + skip_list (); + + get_module_locus (&user_operators); + skip_list (); + skip_list (); + + mio_lparen (); + + /* Create the fixup nodes for all the symbols. */ + + while (peek_atom () != ATOM_RPAREN) + { + require_atom (ATOM_INTEGER); + info = get_integer (atom_int); + + info->type = P_SYMBOL; + info->u.rsym.state = UNUSED; + + mio_internal_string (info->u.rsym.true_name); + mio_internal_string (info->u.rsym.module); + + require_atom (ATOM_INTEGER); + info->u.rsym.ns = atom_int; + + get_module_locus (&info->u.rsym.where); + skip_list (); + + /* See if the symbol has already been loaded by a previous module. + If so, we reference the existing symbol and prevent it from + being loaded again. */ + + sym = find_true_name (info->u.rsym.true_name, info->u.rsym.module); + if (sym == NULL) + continue; + + info->u.rsym.state = USED; + info->u.rsym.referenced = 1; + info->u.rsym.sym = sym; + } + + mio_rparen (); + + /* Parse the symtree lists. This lets us mark which symbols need to + be loaded. Renaming is also done at this point by replacing the + symtree name. */ + + mio_lparen (); + + while (peek_atom () != ATOM_RPAREN) + { + mio_internal_string (name); + mio_integer (&ambiguous); + mio_integer (&symbol); + + info = get_integer (symbol); + + /* Get the local name for this symbol. */ + p = find_use_name (name); + + /* Skip symtree nodes not in an ONLY caluse. */ + if (p == NULL) + continue; + + /* Check for ambiguous symbols. */ + st = gfc_find_symtree (gfc_current_ns->sym_root, p); + + if (st != NULL) + { + if (st->n.sym != info->u.rsym.sym) + st->ambiguous = 1; + info->u.rsym.symtree = st; + } + else + { + /* Create a symtree node in the current namespace for this symbol. */ + st = check_unique_name (p) ? get_unique_symtree (gfc_current_ns) : + gfc_new_symtree (&gfc_current_ns->sym_root, p); + + st->ambiguous = ambiguous; + + sym = info->u.rsym.sym; + + /* Create a symbol node if it doesn't already exist. */ + if (sym == NULL) + { + sym = info->u.rsym.sym = + gfc_new_symbol (info->u.rsym.true_name, gfc_current_ns); + + strcpy (sym->module, info->u.rsym.module); + } + + st->n.sym = sym; + st->n.sym->refs++; + + /* Store the symtree pointing to this symbol. */ + info->u.rsym.symtree = st; + + if (info->u.rsym.state == UNUSED) + info->u.rsym.state = NEEDED; + info->u.rsym.referenced = 1; + } + } + + mio_rparen (); + + /* Load intrinsic operator interfaces. */ + set_module_locus (&operator_interfaces); + mio_lparen (); + + for (i = GFC_INTRINSIC_BEGIN; i != GFC_INTRINSIC_END; i++) + { + if (i == INTRINSIC_USER) + continue; + + if (only_flag) + { + u = find_use_operator (i); + + if (u == NULL) + { + skip_list (); + continue; + } + + u->found = 1; + } + + mio_interface (&gfc_current_ns->operator[i]); + } + + mio_rparen (); + + /* Load generic and user operator interfaces. These must follow the + loading of symtree because otherwise symbols can be marked as + ambiguous. */ + + set_module_locus (&user_operators); + + load_operator_interfaces (); + load_generic_interfaces (); + + /* At this point, we read those symbols that are needed but haven't + been loaded yet. If one symbol requires another, the other gets + marked as NEEDED if its previous state was UNUSED. */ + + while (load_needed (pi_root)); + + /* Make sure all elements of the rename-list were found in the + module. */ + + for (u = gfc_rename_list; u; u = u->next) + { + if (u->found) + continue; + + if (u->operator == INTRINSIC_NONE) + { + gfc_error ("Symbol '%s' referenced at %L not found in module '%s'", + u->use_name, &u->where, module_name); + continue; + } + + if (u->operator == INTRINSIC_USER) + { + gfc_error + ("User operator '%s' referenced at %L not found in module '%s'", + u->use_name, &u->where, module_name); + continue; + } + + gfc_error + ("Intrinsic operator '%s' referenced at %L not found in module " + "'%s'", gfc_op2string (u->operator), &u->where, module_name); + } + + gfc_check_interfaces (gfc_current_ns); + + /* Clean up symbol nodes that were never loaded, create references + to hidden symbols. */ + + read_cleanup (pi_root); +} + + +/* Given an access type that is specific to an entity and the default + access, return nonzero if we should write the entity. */ + +static int +check_access (gfc_access specific_access, gfc_access default_access) +{ + + if (specific_access == ACCESS_PUBLIC) + return 1; + if (specific_access == ACCESS_PRIVATE) + return 0; + + if (gfc_option.flag_module_access_private) + { + if (default_access == ACCESS_PUBLIC) + return 1; + } + else + { + if (default_access != ACCESS_PRIVATE) + return 1; + } + + return 0; +} + + +/* Write a symbol to the module. */ + +static void +write_symbol (int n, gfc_symbol * sym) +{ + + if (sym->attr.flavor == FL_UNKNOWN || sym->attr.flavor == FL_LABEL) + gfc_internal_error ("write_symbol(): bad module symbol '%s'", sym->name); + + mio_integer (&n); + mio_internal_string (sym->name); + + if (sym->module[0] == '\0') + strcpy (sym->module, module_name); + + mio_internal_string (sym->module); + mio_pointer_ref (&sym->ns); + + mio_symbol (sym); + write_char ('\n'); +} + + +/* Recursive traversal function to write the initial set of symbols to + the module. We check to see if the symbol should be written + according to the access specification. */ + +static void +write_symbol0 (gfc_symtree * st) +{ + gfc_symbol *sym; + pointer_info *p; + + if (st == NULL) + return; + + write_symbol0 (st->left); + write_symbol0 (st->right); + + sym = st->n.sym; + + if (sym->attr.flavor == FL_PROCEDURE && sym->attr.generic + && !sym->attr.subroutine && !sym->attr.function) + return; + + if (!check_access (sym->attr.access, sym->ns->default_access)) + return; + + p = get_pointer (sym); + if (p->type == P_UNKNOWN) + p->type = P_SYMBOL; + + if (p->u.wsym.state == WRITTEN) + return; + + write_symbol (p->integer, sym); + p->u.wsym.state = WRITTEN; + + return; +} + + +/* Recursive traversal function to write the secondary set of symbols + to the module file. These are symbols that were not public yet are + needed by the public symbols or another dependent symbol. The act + of writing a symbol can modify the pointer_info tree, so we cease + traversal if we find a symbol to write. We return nonzero if a + symbol was written and pass that information upwards. */ + +static int +write_symbol1 (pointer_info * p) +{ + + if (p == NULL) + return 0; + + if (write_symbol1 (p->left)) + return 1; + if (write_symbol1 (p->right)) + return 1; + + if (p->type != P_SYMBOL || p->u.wsym.state != NEEDS_WRITE) + return 0; + + p->u.wsym.state = WRITTEN; + write_symbol (p->integer, p->u.wsym.sym); + + return 1; +} + + +/* Write operator interfaces associated with a symbol. */ + +static void +write_operator (gfc_user_op * uop) +{ + static char nullstring[] = ""; + + if (uop->operator == NULL + || !check_access (uop->access, uop->ns->default_access)) + return; + + mio_symbol_interface (uop->name, nullstring, &uop->operator); +} + + +/* Write generic interfaces associated with a symbol. */ + +static void +write_generic (gfc_symbol * sym) +{ + + if (sym->generic == NULL + || !check_access (sym->attr.access, sym->ns->default_access)) + return; + + mio_symbol_interface (sym->name, sym->module, &sym->generic); +} + + +static void +write_symtree (gfc_symtree * st) +{ + gfc_symbol *sym; + pointer_info *p; + + sym = st->n.sym; + if (!check_access (sym->attr.access, sym->ns->default_access) + || (sym->attr.flavor == FL_PROCEDURE && sym->attr.generic + && !sym->attr.subroutine && !sym->attr.function)) + return; + + if (check_unique_name (st->name)) + return; + + p = find_pointer (sym); + if (p == NULL) + gfc_internal_error ("write_symtree(): Symbol not written"); + + mio_internal_string (st->name); + mio_integer (&st->ambiguous); + mio_integer (&p->integer); +} + + +static void +write_module (void) +{ + gfc_intrinsic_op i; + + /* Write the operator interfaces. */ + mio_lparen (); + + for (i = GFC_INTRINSIC_BEGIN; i != GFC_INTRINSIC_END; i++) + { + if (i == INTRINSIC_USER) + continue; + + mio_interface (check_access (gfc_current_ns->operator_access[i], + gfc_current_ns->default_access) + ? &gfc_current_ns->operator[i] : NULL); + } + + mio_rparen (); + write_char ('\n'); + write_char ('\n'); + + mio_lparen (); + gfc_traverse_user_op (gfc_current_ns, write_operator); + mio_rparen (); + write_char ('\n'); + write_char ('\n'); + + mio_lparen (); + gfc_traverse_ns (gfc_current_ns, write_generic); + mio_rparen (); + write_char ('\n'); + write_char ('\n'); + + /* Write symbol information. First we traverse all symbols in the + primary namespace, writing those that need to be written. + Sometimes writing one symbol will cause another to need to be + written. A list of these symbols ends up on the write stack, and + we end by popping the bottom of the stack and writing the symbol + until the stack is empty. */ + + mio_lparen (); + + write_symbol0 (gfc_current_ns->sym_root); + while (write_symbol1 (pi_root)); + + mio_rparen (); + + write_char ('\n'); + write_char ('\n'); + + mio_lparen (); + gfc_traverse_symtree (gfc_current_ns, write_symtree); + mio_rparen (); +} + + +/* Given module, dump it to disk. If there was an error while + processing the module, dump_flag will be set to zero and we delete + the module file, even if it was already there. */ + +void +gfc_dump_module (const char *name, int dump_flag) +{ + char filename[PATH_MAX], *p; + gfc_file *g; + time_t now; + + filename[0] = '\0'; + if (gfc_option.module_dir != NULL) + strcpy (filename, gfc_option.module_dir); + + strcat (filename, name); + strcat (filename, MODULE_EXTENSION); + + if (!dump_flag) + { + unlink (filename); + return; + } + + module_fp = fopen (filename, "w"); + if (module_fp == NULL) + gfc_fatal_error ("Can't open module file '%s' for writing: %s", + filename, strerror (errno)); + + /* Find the top level filename. */ + g = gfc_current_file; + while (g->next) + g = g->next; + + now = time (NULL); + p = ctime (&now); + + *strchr (p, '\n') = '\0'; + + fprintf (module_fp, "GFORTRAN module created from %s on %s\n", g->filename, p); + fputs ("If you edit this, you'll get what you deserve.\n\n", module_fp); + + iomode = IO_OUTPUT; + strcpy (module_name, name); + + init_pi_tree (); + + write_module (); + + free_pi_tree (pi_root); + pi_root = NULL; + + write_char ('\n'); + + if (fclose (module_fp)) + gfc_fatal_error ("Error writing module file '%s' for writing: %s", + filename, strerror (errno)); +} + + +/* Process a USE directive. */ + +void +gfc_use_module (void) +{ + char filename[GFC_MAX_SYMBOL_LEN + 5]; + gfc_state_data *p; + int c, line; + + strcpy (filename, module_name); + strcat (filename, MODULE_EXTENSION); + + module_fp = gfc_open_included_file (filename); + if (module_fp == NULL) + gfc_fatal_error ("Can't open module file '%s' for reading: %s", + filename, strerror (errno)); + + iomode = IO_INPUT; + module_line = 1; + module_column = 1; + + /* Skip the first two lines of the module. */ + /* FIXME: Could also check for valid two lines here, instead. */ + line = 0; + while (line < 2) + { + c = module_char (); + if (c == EOF) + bad_module ("Unexpected end of module"); + if (c == '\n') + line++; + } + + /* Make sure we're not reading the same module that we may be building. */ + for (p = gfc_state_stack; p; p = p->previous) + if (p->state == COMP_MODULE && strcmp (p->sym->name, module_name) == 0) + gfc_fatal_error ("Can't USE the same module we're building!"); + + init_pi_tree (); + init_true_name_tree (); + + read_module (); + + free_true_name (true_name_root); + true_name_root = NULL; + + free_pi_tree (pi_root); + pi_root = NULL; + + fclose (module_fp); +} + + +void +gfc_module_init_2 (void) +{ + + last_atom = ATOM_LPAREN; +} + + +void +gfc_module_done_2 (void) +{ + + free_rename (); +} |