[m-rev.] for review: standalone interfaces

Julien Fischer juliensf at csse.unimelb.edu.au
Mon Feb 5 17:41:21 AEDT 2007


There is no documentation for the following yet, however Peter Ross has been
requesting this (and presumably he can work out how to use it for himself.)

Also, while working on this one of the problems I ran into is that the compiler
sets the standard output stream to be different things at different points.
This is quite confusing and extremely annoying to debug if it goes wrong.
I would like to (a) change the compiler so that all streams are explicit
arguments and (b) change the coding standards to say that implicit streams
shouldn't be used.  (It's quite large change but it should be fairly trivial.)
Objections?

For review by anyone.

Estimated hours taken: 20
Branches: main

Improve support for calling procedures in Mercury libraries from
applications written in foreign languages, i.e where the program entry point
is not the Mercury predicate main/2.  The main trick is to ensure that the
necessary runtime initialisation is done before any Mercury procedures are
called (or at least to provide the mechanism to do such initialisation,
ensuring that it is done is the programmer's responsibility.)

We currently support this sort of thing via the compiler's `--no-main'
option.  This diff adds a more user friendly mechanism (at least on the
Mercury side.)  In particular, we no longer require that one of the Mercury
libraries define a main/2 predicate.  (Note: the existing behaviour of
--no-main is unchanged.)

For the set of Mercury libraries that we wish to use from a foreign
application we create a standalone interface.  A standalone interface is a
cut-down version of the _init.c file that would be created for a Mercury
executable that uses the same set of libraries.  (Which libraries to include
in the interface can be specified via the usual mechanisms, e.g. the --ml
option.)

The standalone interface has two parts: an object file that contains the
cut-down version of the _init.c file, and a header file that contains the
declarations for the functions that initialise and shut down the Mercury
runtime.  This header file is compatible with both C and C++.

compiler/options.m:
 	Add a new option `--generate-standalone-interface' that causes the
 	compiler to generate a header/object pair that can be used to
 	intialise/shut down the Mercury runtime from a foreign application.
 	The basename of the header/object pair is given as an argument to this
 	option.

compiler/mercury_compile.m:
 	Create the standalone interface if invoked with
 	`--generate-standalone-interface'.

 	Emit an error message if `--generate-standalone-interface' is
 	specified with `--target java' or `--target il'.  We don't
 	currently support that.

compiler/compile_target_code.m:
 	Add code to implement the `--generate-standalone-interface' option.

 	Fix a typo: s/Serarator/Separator/.

 	Fix an overlong line.

compiler/handle_options.m:
 	Emit an error message if `--generate-standalone-interface' and
 	`--extra-inits' are specified together.

util/mkinit.c:
 	Add a new mode of operation that generates standalone interfaces.
 	The principle differences between a standalone interface and a
 	_init.c file are that the former sets the program entry point to
 	MR_dummy_main and does not create a main function.

 	The new `-s' option tells mkinit to create a standalone interface.
 	(Note that `-s' implies `-l'.)

 	Add a comment pointing to various places that may need to updated if
 	mkinit.c is changed.

runtime/mercury_wrapper.{h,c}:
 	Add a new procedure MR_dummy_main for use with standalone
 	interfaces.  Any attempt to call main/2 through the usual entry
 	point when operating in standalone mode will cause a runtime abort.

Julien.

Index: compiler/compile_target_code.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/compile_target_code.m,v
retrieving revision 1.103
diff -u -r1.103 compile_target_code.m
--- compiler/compile_target_code.m	8 Jan 2007 03:03:08 -0000	1.103
+++ compiler/compile_target_code.m	5 Feb 2007 06:22:33 -0000
@@ -167,6 +167,14 @@
      is det.

  %-----------------------------------------------------------------------------%
+ 
+    % make_standalone_interface(Basename, !IO):
+    %
+    % Create a standalone interface in the current directory.
+    % 
+:- pred make_standalone_interface(string::in, io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
  %-----------------------------------------------------------------------------%

  :- implementation.
@@ -514,7 +522,7 @@
      (
          RecordTermSizesAsWords = yes,
          RecordTermSizesAsCells = yes,
-        % this should have been caught in handle_options
+        % This should have been caught in handle_options.
          unexpected(this_file,
              "compile_c_file: inconsistent record term size options")
      ;
@@ -1051,6 +1059,7 @@

  % WARNING: The code here duplicates the functionality of scripts/c2init.in.
  % Any changes there may also require changes here, and vice versa.
+% The code of make_standalone_interface/3 may also require updating.

  :- pred make_init_obj_file(io.output_stream::in, bool::in,
      module_name::in, list(module_name)::in, maybe(file_name)::out,
@@ -1882,7 +1891,7 @@

  %-----------------------------------------------------------------------------%

-    % join_string_list(Strings, Prefix, Suffix, Serarator, Result):
+    % join_string_list(Strings, Prefix, Suffix, Separator, Result):
      %
      % Appends the strings in the list `Strings' together into the string
      % Result. Each string is prefixed by Prefix, suffixed by Suffix and
@@ -1985,6 +1994,150 @@
      maybe_pic_object_file_extension(Globals, PIC, ObjExt).

  %-----------------------------------------------------------------------------%
+%
+% Standalone interfaces
+%
+
+% NOTE: the following code is similar to that of make_init_obj/7.  Any
+% changes here may need to be reflected there.
+
+make_standalone_interface(Basename, !IO) :-
+    make_standalone_int_header(Basename, HdrSucceeded, !IO),
+    (
+        HdrSucceeded = yes,
+        make_standalone_int_body(Basename, !IO)
+    ;
+        HdrSucceeded = no
+    ).
+
+:- pred make_standalone_int_header(string::in, bool::out,
+    io::di, io::uo) is det.
+
+make_standalone_int_header(Basename, Succeeded, !IO) :-
+    HdrFileName = Basename ++ ".h", 
+    io.open_output(HdrFileName, OpenResult, !IO),
+    (
+        OpenResult = ok(HdrFileStream),
+        io.write_strings(HdrFileStream, [
+            "#ifndef ", to_upper(Basename), "_H\n",
+            "#define ", to_upper(Basename), "_H\n",
+            "\n",
+            "#ifdef __cplusplus\n",
+            "extern \"C\" {\n",
+            "#endif\n",
+            "\n",
+            "extern void\n",
+            "mercury_init(int argc, char **argv, void *stackbottom);\n",
+            "\n",
+            "extern int\n",
+            "mercury_terminate(void);\n",
+            "\n",
+            "#ifdef __cplusplus\n",
+            "}\n",
+            "#endif\n",
+            "\n",
+            "#endif /* ", to_upper(Basename), "_H */\n"],
+            !IO),
+        io.close_output(HdrFileStream, !IO),
+        Succeeded = yes
+    ;
+        OpenResult = error(Error),
+        unable_to_open_file(HdrFileName, Error, !IO),
+        Succeeded = no
+    ).
+
+:- pred make_standalone_int_body(string::in, io::di, io::uo) is det.
+
+make_standalone_int_body(Basename, !IO) :-
+    globals.io_get_globals(Globals, !IO),
+    globals.lookup_accumulating_option(Globals, init_files, InitFiles0),
+    globals.lookup_accumulating_option(Globals, trace_init_files,
+        TraceInitFiles0),
+    globals.lookup_maybe_string_option(Globals,
+        mercury_standard_library_directory, MaybeStdLibDir),
+    grade_directory_component(Globals, GradeDir),
+    (
+        MaybeStdLibDir = yes(StdLibDir),
+        InitFiles1 = [
+            StdLibDir / "modules" / GradeDir / "mer_rt.init",
+            StdLibDir / "modules" / GradeDir / "mer_std.init" |
+            InitFiles0
+        ],
+        TraceInitFiles = [
+            StdLibDir / "modules" / GradeDir / "mer_browser.init",
+            StdLibDir / "modules" / GradeDir / "mer_mdbcomp.init" |
+            TraceInitFiles0
+        ]
+    ;
+        % Supporting `--no-mercury-standard-library-directory' is necessary
+        % in order to use `--generate-standalone-interface' with the
+        % the lmc script.
+        MaybeStdLibDir = no,
+        InitFiles1 = InitFiles0,
+        TraceInitFiles = TraceInitFiles0
+    ),
+    globals.get_trace_level(Globals, TraceLevel),
+    ( given_trace_level_is_none(TraceLevel) = no ->
+        TraceOpt = "-t",
+        InitFiles = InitFiles1 ++ TraceInitFiles
+    ;
+        TraceOpt = "",
+        InitFiles = InitFiles1
+    ),
+    join_string_list(InitFiles, "", "", " ", InitFilesList),
+    globals.lookup_accumulating_option(Globals, runtime_flags,
+        RuntimeFlagsList),
+    join_quoted_string_list(RuntimeFlagsList, "-r ", "", " ", RuntimeFlags),
+    globals.lookup_string_option(Globals, experimental_complexity,
+        ExperimentalComplexity),
+    ( ExperimentalComplexity = "" ->
+        ExperimentalComplexityOpt = ""
+    ;
+        ExperimentalComplexityOpt = "-X " ++ ExperimentalComplexity
+    ),
+    compute_grade(Globals, Grade),
+    globals.lookup_string_option(Globals, mkinit_command, MkInit),
+    CFileName = Basename ++ ".c",
+    io.output_stream(ErrorStream, !IO),
+    MkInitCmd = string.append_list(
+        [   MkInit,
+            " -g ", Grade,
+            " ", TraceOpt,
+            " ", ExperimentalComplexityOpt,
+            " ", RuntimeFlags,
+            " -o ", quote_arg(CFileName),
+            " -s ", InitFilesList
+        ]),
+    invoke_system_command(ErrorStream, cmd_verbose, MkInitCmd, MkInitCmdOk,
+        !IO),
+    (
+        MkInitCmdOk = yes,
+        get_object_code_type(executable, PIC, !IO),
+        maybe_pic_object_file_extension(PIC, ObjExt, !IO),
+        ObjFileName = Basename ++ ObjExt,
+        compile_c_file(ErrorStream, PIC, CFileName, ObjFileName,
+            CompileOk, !IO),
+        (
+            CompileOk = yes
+        ;
+            CompileOk = no,
+            io.set_exit_status(1, !IO),
+            io.write_string("mercury_compile: error while compiling ", !IO),
+            io.write_string("standalone interface in `", !IO),
+            io.write_string(CFileName, !IO),
+            io.write_string("'\n", !IO)
+        )
+    ;
+        MkInitCmdOk = no,
+        io.set_exit_status(1, !IO),
+        io.write_string("mercury_compile: error while creating ", !IO),
+        io.write_string("standalone interface in `", !IO),
+        io.write_string(CFileName, !IO),
+        io.write_string("'\n", !IO)
+    ).
+
+%-----------------------------------------------------------------------------%

  :- func this_file = string.

Index: compiler/handle_options.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/handle_options.m,v
retrieving revision 1.294
diff -u -r1.294 handle_options.m
--- compiler/handle_options.m	29 Jan 2007 04:31:42 -0000	1.294
+++ compiler/handle_options.m	5 Feb 2007 04:30:55 -0000
@@ -771,6 +771,21 @@
              globals.set_option(use_symlinks, bool(no), !Globals)
          ),

+        globals.lookup_maybe_string_option(!.Globals,
+            generate_standalone_interface, MaybeStandaloneInt),
+        globals.lookup_bool_option(!.Globals,
+            extra_initialization_functions, ExtraInitFunctions),
+        (
+            MaybeStandaloneInt = yes(_),
+            ExtraInitFunctions = yes
+        ->
+            add_error("`--generate-standalone-interface' is" ++
+                " incompatible with `--extra-initialization-functions'.",
+                !Errors)
+        ;
+            true
+        ),
+
          option_implies(structure_reuse_analysis, structure_sharing_analysis,
              bool(yes), !Globals),
          option_implies(verbose_check_termination, termination_check, bool(yes),
Index: compiler/mercury_compile.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mercury_compile.m,v
retrieving revision 1.426
diff -u -r1.426 mercury_compile.m
--- compiler/mercury_compile.m	23 Jan 2007 03:45:34 -0000	1.426
+++ compiler/mercury_compile.m	5 Feb 2007 06:18:17 -0000
@@ -374,6 +374,8 @@
      globals.lookup_bool_option(Globals, output_libgrades,
          OutputLibGrades),
      globals.lookup_bool_option(Globals, make, Make),
+    globals.lookup_maybe_string_option(Globals, generate_standalone_interface,
+        GenerateStandaloneInt),
      ( Version = yes ->
          io.stdout_stream(Stdout, !IO),
          io.set_output_stream(Stdout, OldOutputStream, !IO),
@@ -410,6 +412,28 @@
          io.write_list(Stdout, LibGrades, "\n", io.write_string, !IO)
      ; GenerateMapping = yes ->
          source_file_map.write_source_file_map(Args, !IO)
+    ; GenerateStandaloneInt = yes(StandaloneIntBasename) ->
+        globals.io_get_target(Target, !IO),
+        (
+            ( Target = target_il
+            ; Target = target_java
+            ),
+            NYIMsg = [
+                words("Sorry,"),
+                quote("--generate-standalone-interface"), 
+                words("is not yet supported with target language"),
+                words(compilation_target_string(Target)),
+                suffix(".")
+            ],
+            write_error_pieces_plain(NYIMsg, !IO),
+            io.set_exit_status(1, !IO)
+        ; 
+            ( Target = target_c
+            ; Target = target_asm
+            ; Target = target_x86_64
+            ),
+            make_standalone_interface(StandaloneIntBasename, !IO) 
+        )
      ; Make = yes ->
          make_process_args(OptionVariables, OptionArgs, Args, !IO)
      ; Args = [], FileNamesFromStdin = no ->
@@ -3923,8 +3947,8 @@
              globals.get_target(Globals, Target),
              (
                  Target = target_c,
-                maybe_write_string(Verbose, "% Applying implicit parallelism...\n"
-                    , !IO), 
+                maybe_write_string(Verbose, "% Applying implicit " ++
+                    "parallelism...\n", !IO),
                  maybe_flush_output(Verbose, !IO),
                  apply_implicit_parallelism_transformation(!HLDS,
                      FeedbackFile, !IO),
Index: compiler/options.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/options.m,v
retrieving revision 1.546
diff -u -r1.546 options.m
--- compiler/options.m	19 Jan 2007 07:04:25 -0000	1.546
+++ compiler/options.m	5 Feb 2007 06:05:43 -0000
@@ -169,6 +169,7 @@
      ;       generate_dependency_file
      ;       generate_dependencies
      ;       generate_module_order
+    ;       generate_standalone_interface
      ;       convert_to_mercury
      ;       typecheck_only
      ;       errorcheck_only
@@ -943,6 +944,7 @@
      generate_dependency_file            -   bool(no),
      generate_dependencies               -   bool(no),
      generate_module_order               -   bool(no),
+    generate_standalone_interface       -   maybe_string(no),
      make_short_interface                -   bool(no),
      make_interface                      -   bool(no),
      make_private_interface              -   bool(no),
@@ -1671,6 +1673,7 @@
  long_option("generate-dependency-file", generate_dependency_file).
  long_option("generate-dependencies",    generate_dependencies).
  long_option("generate-module-order",    generate_module_order).
+long_option("generate-standalone-interface", generate_standalone_interface).
  long_option("make-short-interface", make_short_interface).
  long_option("make-short-int",       make_short_interface).
  long_option("make-interface",       make_interface).
@@ -3078,6 +3081,12 @@
          "\tOutput the strongly connected components of the module",
          "\tdependency graph in top-down order to `<module>.order'.",
          "\tImplies --generate-dependencies.",
+        % XXX This documentation is commented out until the user's
+        % guide is updated.
+        %"--generate-standalone-interface <name>",
+        %"\tOutput an object file that can be used to embed the Mercury",
+        %"\truntime in an application written in a foreign language.",
+        %"\tSee Users's guide for more details.",
          "-i, --make-int, --make-interface",
          "\tWrite the module interface to `<module>.int',",
          "\tand write the short interface to `<module>.int2'",
Index: compiler/passes_aux.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/passes_aux.m,v
retrieving revision 1.88
diff -u -r1.88 passes_aux.m
--- compiler/passes_aux.m	19 Jan 2007 07:04:25 -0000	1.88
+++ compiler/passes_aux.m	5 Feb 2007 04:48:56 -0000
@@ -513,7 +513,7 @@
                      ProcessedTmpFile, " 2>&1"])
          ),
          io.call_system_return_signal(ProcessOutputRedirected,
-                ProcessOutputResult, !IO),
+            ProcessOutputResult, !IO),
          io.remove_file(TmpFile, _, !IO),
          (
              ProcessOutputResult = ok(exited(ProcessOutputStatus)),
Index: runtime/mercury_wrapper.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_wrapper.c,v
retrieving revision 1.178
diff -u -r1.178 mercury_wrapper.c
--- runtime/mercury_wrapper.c	12 Jan 2007 05:00:31 -0000	1.178
+++ runtime/mercury_wrapper.c	2 Feb 2007 15:43:25 -0000
@@ -2632,3 +2632,28 @@
      /* no proc_statics to write out */
  }
  #endif
+
+/*---------------------------------------------------------------------------*/
+
+#ifndef MR_HIGHLEVEL_CODE
+
+MR_define_extern_entry(MR_dummy_main);
+
+MR_BEGIN_MODULE(dummy_main_module)
+        MR_init_entry_an(MR_dummy_main);
+MR_BEGIN_CODE
+
+MR_define_entry(MR_dummy_main);
+        MR_fatal_error("invalid attempt to call through Mercury entry point.");
+
+MR_END_MODULE
+
+#else   /* MR_HIGHLEVEL_CODE */
+
+void
+MR_dummy_main(void)
+{
+    MR_fatal_error("invalid attempt to call through Mercury entry point.");
+}
+
+#endif /* MR_HIGHLEVEL_CODE */
Index: runtime/mercury_wrapper.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_wrapper.h,v
retrieving revision 1.78
diff -u -r1.78 mercury_wrapper.h
--- runtime/mercury_wrapper.h	29 Nov 2006 05:18:28 -0000	1.78
+++ runtime/mercury_wrapper.h	7 Dec 2006 00:26:27 -0000
@@ -71,6 +71,10 @@
  			/* normally mercury__main_2_0; */
  #endif

+#ifdef MR_HIGHLEVEL_CODE
+extern void MR_dummy_main(void);
+#endif
+
  extern const char	*MR_runtime_flags;

  extern	void		(*MR_library_initializer)(void);
Index: util/mkinit.c
===================================================================
RCS file: /home/mercury1/repository/mercury/util/mkinit.c,v
retrieving revision 1.114
diff -u -r1.114 mkinit.c
--- util/mkinit.c	10 Oct 2006 08:26:04 -0000	1.114
+++ util/mkinit.c	5 Feb 2007 06:25:08 -0000
@@ -20,6 +20,18 @@
  ** list of intialization directives on stdout.  This mode of operation is
  ** is used when building .init files for libraries.
  **
+** If invoked with the -s option, this program produces a standalone 
+** runtime interface on stdout.  This mode of operation is used when
+** using Mercury libraries from applications written in foreign languages.
+**
+** NOTE: any changes to this program may need to be reflected in the
+** following places:
+**
+**      - scripts/c2init.in
+**      - compiler/compile_target_code.m
+**          in particular the predicates make_init_obj/7 and 
+**          make_standalone_interface/3.
+**
  */

  /*---------------------------------------------------------------------------*/
@@ -78,6 +90,13 @@

  typedef enum
  {
+    TASK_OUTPUT_INIT_PROG = 0,
+    TASK_OUTPUT_LIB_INIT  = 1,
+    TASK_OUTPUT_STANDALONE_INIT = 2
+} Task;
+ 
+typedef enum
+{
      PURPOSE_INIT = 0,
      PURPOSE_TYPE_TABLE = 1,
      PURPOSE_DEBUGGER = 2,
@@ -278,7 +297,7 @@
  static MR_bool      output_main_func = MR_TRUE;
  static MR_bool      need_initialization_code = MR_FALSE;
  static MR_bool      need_tracing = MR_FALSE;
-static MR_bool      output_lib_init = MR_FALSE;
+static Task         output_task = TASK_OUTPUT_INIT_PROG;
  static const char   *experimental_complexity = NULL;

  static int          num_experimental_complexity_procs = 0;
@@ -478,18 +497,27 @@
      "   return;\n"
      "}\n"
      "\n"
+    ;
+ 
+static const char mercury_call_main_func[] =
      "void\n"
      "mercury_call_main(void)\n"
      "{\n"
      "   mercury_runtime_main();\n"
      "}\n"
      "\n"
+    ;
+ 
+static const char mercury_terminate_func[] =
      "int\n"
      "mercury_terminate(void)\n"
      "{\n"
      "   return mercury_runtime_terminate();\n"
      "}\n"
      "\n"
+    ;
+
+static const char mercury_main_func[] =
      "int\n"
      "mercury_main(int argc, char **argv)\n"
      "{\n"
@@ -505,8 +533,12 @@
      "   return mercury_terminate();\n"
      "}\n"
      "\n"
+    ;
+
+static const char mercury_grade_var[] =
      "/* ensure that everything gets compiled in the same grade */\n"
      "static const void *const MR_grade = &MR_GRADE_VAR;\n"
+    "\n"
      ;

  static const char main_func[] =
@@ -596,12 +628,26 @@

      set_output_file();

-    if (output_lib_init) {
-        exit_status = output_lib_init_file();  /* Output a .init file. */
-    } else {
-        exit_status = output_init_program();   /* Output a _init.c file. */
+    switch (output_task) {
+        case TASK_OUTPUT_LIB_INIT:
+            /* Output a .init file */
+            exit_status = output_lib_init_file();
+            break;
+ 
+        case TASK_OUTPUT_STANDALONE_INIT:
+        case TASK_OUTPUT_INIT_PROG:
+            /*
+            ** Output a _init.c file or a standalone initialisation 
+            ** interface.
+            */
+            exit_status = output_init_program();
+            break;
+ 
+        default:
+            fprintf(stderr, "mkinit: unknown task\n");
+            exit(EXIT_FAILURE);
      }
-
+
      return exit_status;
  }

@@ -738,7 +784,7 @@
      int         i;
      String_List *tmp_slist;

-    while ((c = getopt(argc, argv, "A:c:g:iI:lo:r:tw:xX:k")) != EOF) {
+    while ((c = getopt(argc, argv, "A:c:g:iI:lo:r:tw:xX:ks")) != EOF) {
          switch (c) {
          case 'A':
              /*
@@ -829,7 +875,12 @@
              break;

          case 'k':
-            output_lib_init = MR_TRUE;
+            output_task = TASK_OUTPUT_LIB_INIT;
+            break;
+
+        case 's':
+            output_task = TASK_OUTPUT_STANDALONE_INIT;
+            output_main_func = MR_FALSE; /* -s implies -l */
              break;

          default:
@@ -860,6 +911,7 @@
      fputs("  -w entry:\tset the entry point to the given label\n", stderr);
      fputs("  -I dir:\tadd dir to the search path for init files\n", stderr);
      fputs("  -k:\t\tgenerate the .init for a library\n", stderr);
+    fputs("  -s:\t\tgenerate a standalone runtime interface\n", stderr);
      exit(EXIT_FAILURE);
  }

@@ -1159,6 +1211,17 @@
          printf("};\n");
      }

+    /*
+    ** If we are building a standalone interface then we set the entry
+    ** point as MR_dummy_main.  This is defined in the
+    ** runtime/mercury_wrapper.c and aborts execution if called.  In
+    ** standalone mode we are not expecting Mercury to be called through the
+    ** standard entry point.
+    */
+    if (output_task == TASK_OUTPUT_STANDALONE_INIT) {
+        hl_entry_point = entry_point = "MR_dummy_main";
+    }
+
      printf(mercury_funcs1, hl_entry_point, entry_point);
      printf(mercury_funcs2, num_experimental_complexity_procs,
          hl_entry_point, entry_point);
@@ -1200,7 +1263,19 @@
      }

      fputs(mercury_funcs4, stdout);
-
+ 
+    if (output_task == TASK_OUTPUT_INIT_PROG) {
+        fputs(mercury_call_main_func, stdout);
+    }
+ 
+    fputs(mercury_terminate_func, stdout);
+ 
+    if (output_task == TASK_OUTPUT_INIT_PROG) {
+        fputs(mercury_main_func, stdout);
+    } 
+ 
+    fputs(mercury_grade_var, stdout);
+
      if (output_main_func) {
          fputs(main_func, stdout);
      }

--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list