#include "a68.h"
+/* A few macros to aid parsing of module map strings below. */
+
+#define SKIP_WHITESPACES(P) while (ISSPACE (*(P))) (P)++
+
+#define PARSE_BASENAME(P,W) \
+ do \
+ { \
+ (W) = (char *) alloca (strlen ((P))); \
+ size_t i = 0; \
+ while ((*(P)) != '=' && !ISSPACE (*(P)) && ((*(P)) != '\0')) \
+ (W)[i++] = *((P)++); \
+ (W)[i] = '\0'; \
+ } while (0)
+
+#define PARSE_INDICANT(P,W) \
+ do \
+ { \
+ (W) = (char *) alloca (strlen ((P))); \
+ size_t i = 0; \
+ if (ISALPHA (*(P))) \
+ { \
+ (W)[i++] = *((P)++); \
+ while (ISALPHA (*(P)) || ISDIGIT(*(P)) || (*(P)) == '_') \
+ { \
+ if ((*(P)) != '_') \
+ (W)[i++] = *((P)++); \
+ } \
+ } \
+ (W)[i] = '\0'; \
+ } while (0)
+
+/* Parse module map information in MAP and add entries to A68_MODULE_FILES
+ accordingly. Existing entries in the map are overriden without warning.
+
+ If MAP is not a valid module map specification then this function returns
+ `false' and sets *ERRMSG to some explanatory message. Otherwise it returns
+ `true' and sets *ERRMSG to NULL. */
+
+bool
+a68_process_module_map (const char *map, const char **errmsg)
+{
+ const char *p = map;
+
+ while (*p != '\0')
+ {
+ char *filename;
+ SKIP_WHITESPACES (p);
+ PARSE_BASENAME (p, filename);
+
+ if (p[0] != '=')
+ {
+ *errmsg = "expected = after filename";
+ goto error;
+ }
+ p++;
+
+ /* Parse one or more joined module indicants. */
+ while (p[0] != ':' && p[0] != '\0')
+ {
+ char *module;
+ SKIP_WHITESPACES (p);
+ PARSE_INDICANT (p, module);
+ if (module[0] == '\0')
+ {
+ *errmsg = "expected module indicant";
+ goto error;
+ }
+
+ SKIP_WHITESPACES (p);
+ if (p[0] != ',' && p[0] != ':' && p[0] != '\0')
+ {
+ *errmsg = "expected comma or end of string after module indicant";
+ goto error;
+ }
+
+ for (char *q = module; *q; ++q)
+ *q = TOUPPER (*q);
+ A68_MODULE_FILES->put (ggc_strdup (module), ggc_strdup (filename));
+
+ if (p[0] == ',')
+ p++;
+ }
+
+ SKIP_WHITESPACES (p);
+ if (p[0] != ':' && p[0] != '\0')
+ {
+ *errmsg = "expected semicolon or end of string";
+ goto error;
+ }
+
+ if (p[0] == ':')
+ p++;
+ }
+
+ *errmsg = NULL;
+ return true;
+ error:
+ return false;
+}
+
/* Read exports from an object file.
FD is a file descriptor open for reading.
else
size_type_node = long_unsigned_type_node;
- /* Create an empty module files map. */
- A68_MODULE_FILES = hash_map<nofree_string_hash,const char*>::create_ggc (16);
- A68_MODULE_FILES->empty ();
-
return true;
}
a68_init_options (unsigned int argc ATTRIBUTE_UNUSED,
cl_decoded_option *decoded_options ATTRIBUTE_UNUSED)
{
- /* Nothing to do here for now. */
+ /* Create an empty module files map. */
+ A68_MODULE_FILES = hash_map<nofree_string_hash,const char*>::create_ggc (16);
+ A68_MODULE_FILES->empty ();
}
#undef LANG_HOOKS_INIT_OPTIONS
switch (code)
{
+ case OPT_fmodules_map:
+ case OPT_fmodules_map_:
+ {
+ const char *errmsg;
+ if (!a68_process_module_map (arg, &errmsg))
+ error ("invalid argument for %<-fmodules-map%>: %s", errmsg);
+ break;
+ }
+ case OPT_fmodules_map_file:
+ case OPT_fmodules_map_file_:
+ {
+ FILE *file = fopen (arg, "r");
+ if (file == NULL)
+ fatal_error (UNKNOWN_LOCATION,
+ "cannot open modules map file %<%s%>", arg);
+
+ ssize_t ssize = a68_file_size (file);
+ if (ssize < 0)
+ fatal_error (UNKNOWN_LOCATION,
+ "cannot determine size of modules map file %<%s%>", arg);
+ size_t fsize = ssize;
+
+ char *buffer = (char *) xmalloc (fsize + 1);
+ size_t bytes_read = a68_file_read (file, buffer, fsize);
+ if (bytes_read != fsize)
+ fatal_error (UNKNOWN_LOCATION,
+ "cannot read contents of modules map file %<%s%>", arg);
+ buffer[fsize] = '\0';
+
+ const char *errmsg;
+ if (!a68_process_module_map (buffer, &errmsg))
+ fatal_error (UNKNOWN_LOCATION, "%s: %s", arg, errmsg);
+ free (buffer);
+ break;
+ }
case OPT_std_algol68:
OPTION_STRICT (&A68_JOB) = 1;
break;
found);
return NULL;
}
+
+ /* Normalize module indicant to upper-case. */
+ for (char *q = module; *q; ++q)
+ *q = TOUPPER (*q);
+
/* Add entry in the module files map. */
const char **pmodule = A68_MODULE_FILES->get (module);
if (pmodule != NULL)
/* a68-parser-scanner.cc */
bool a68_lexical_analyser (const char *filename, bool *empty_file);
-ssize_t a68_get_file_size (FILE *file);
+ssize_t a68_file_size (FILE *file);
ssize_t a68_file_read (FILE *file, void *buf, size_t n);
/* a68-parser.cc */
/* a68-imports.cc */
MOIF_T *a68_open_packet (const char *module);
+bool a68_process_module_map (const char *map, const char **errmsg);
/* a68-parser-debug.cc */
@menu
* Dialect options:: Options controlling the accepted language.
* Directory options:: Options influencing where to find source files.
+* Module search options:: Options influencing where to look for modules.
* Warnings options:: Options controlling warnings specific to ga68
* Runtime options:: Options controlling runtime behavior
* Linking options:: Options influencing the linking step
@end table
+@node Module search options
+@section Module search options
+@cindex options, modules
+@cindex modules
+
+The following options can be used to tell the compiler where to look
+for certain modules.
+
+@table @gcctabopt
+@opindex fmodules-map
+@item -fmodules-map=@var{string}
+Use the mapping between module indicants and module base filenames
+specified in @var{string}, which must contain a sequence of entries
+with form
+@code{@var{basename}=@var{moduleindicant}[,@var{moduleindicant}]...}
+separated by colon (@code{:}) characters.
+
+When a module @var{moduleindicant} is accessed, the compiler will look
+for exports information for it in files @file{@var{basename}.m68},
+@file{lib@var{basename}.so}, @file{lib@var{basename}.a},
+@file{@var{basename}.o}, in that order.
+
+This option is used to avoid the default behavior, in which the
+basename used to search for an accessed module is implicitly derived
+from its indicant, by transforming it to lower case.
+
+The effect of this option is accumulative.
+
+@opindex fmodules-map-file
+@item -fmodules-map-file=@var{<filename>}
+Like @option{-fmodules-map}, but read the mapping information from the
+file @var{<filename>}.
+@end table
+
@node Warnings options
@section Warnings options
@cindex options, warnings
XXX
+As we have seen modules are accessed by referring to them in
+access-clauses, using the same sort of bold-word indicants that
+identify user-defined modes and operators, such as @code{JSON},
+@code{Transput} or @code{LEB128_Arithmetic}.
+
@node Modules and protection
@subsection Modules and protection
@cindex protection
EnumValue
Enum(stropping_regime) String(supper) Value(1)
+; Module maps
+
+fmodules-map
+Algol68 Separate RejectNegative
+-fmodules-map=<mapstring> Association between module indicants and files.
+
+fmodules-map=
+Algol68 Joined RejectNegative
+-fmodules-map=<mapstring> Association between module indicants and files.
+
+fmodules-map-file
+Algol68 Separate RejectNegative
+-fmodules-map-file=<filename> File containing modules map.
+
+fmodules-map-file=
+Algol68 Joined RejectNegative
+-fmodules-map-file=<filename> File containing modules map.
+
; This comment is to ensure we retain the blank line above.
--- /dev/null
+module-foo=Foo:
+module-bar=Bar
--- /dev/null
+module Bar = def pub int bar = 20; skip fed
--- /dev/null
+module Foo = def pub int foo = 10; skip fed
--- /dev/null
+{ dg-modules "module-foo module-bar" }
+{ dg-options {-fmodules-map=module-foo=Foo:module-bar=Bar} }
+
+begin access Foo (assert (foo = 10));
+ access Bar (assert (bar = 20))
+end
--- /dev/null
+{ dg-modules "module-foo module-bar" }
+{ dg-options "-fmodules-map-file=$srcdir/algol68/execute/modules/Modules20.map" }
+
+begin access Foo (assert (foo = 10));
+ access Bar (assert (bar = 20))
+end