/* -----------------------------------------------------------------------------
 * This file is part of SWIG, which is licensed as a whole under version 3
 * (or any later version) of the GNU General Public License. Some additional
 * terms also apply to certain portions of SWIG. The full details of the SWIG
 * license and copyrights can be found in the LICENSE and COPYRIGHT files
 * included with the SWIG source code as distributed by the SWIG developers
 * and at https://www.swig.org/legal.html.
 *
 * ocaml.cxx
 *
 * Ocaml language module for SWIG.
 * ----------------------------------------------------------------------------- */

#include "swigmod.h"
#include <ctype.h>

static const char *usage = "\
Ocaml Options (available with -ocaml)\n\
     -oldvarnames    - Old intermediary method names for variable wrappers\n\
     -prefix <name>  - Set a prefix <name> to be prepended to all names\n\
     -where          - Emit library location\n\
\n";

static int classmode = 0;
static int in_constructor = 0, in_destructor = 0, in_copyconst = 0;
static int const_enum = 0;
static int static_member_function = 0;
static int generate_sizeof = 0;
static String *prefix = 0;
static const char *ocaml_path = "ocaml";
static bool old_variable_names = false;
static String *classname = 0;
static String *module = 0;
static String *init_func_def = 0;
static String *f_classtemplate = 0;
static SwigType *name_qualifier_type = 0;

static Hash *seen_enums = 0;
static Hash *seen_enumvalues = 0;
static Hash *seen_constructors = 0;

static File *f_header = 0;
static File *f_begin = 0;
static File *f_runtime = 0;
static File *f_wrappers = 0;
static File *f_directors = 0;
static File *f_directors_h = 0;
static File *f_init = 0;
static File *f_mlout = 0;
static File *f_mliout = 0;
static File *f_mlbody = 0;
static File *f_mlibody = 0;
static File *f_mltail = 0;
static File *f_mlitail = 0;
static File *f_enumtypes_type = 0;
static File *f_enumtypes_value = 0;
static File *f_class_ctors = 0;
static File *f_class_ctors_end = 0;
static File *f_enum_to_int = 0;
static File *f_int_to_enum = 0;

class OCAML : public Language {
public:
  OCAML() {
    director_prot_ctor_code = NewString("");
    Printv(director_prot_ctor_code,
           "if ($comparison) { /* subclassed */\n",
           "  $director_new\n",
           "} else {\n",
           "  caml_failwith(\"accessing abstract class or protected constructor\"); \n",
           "}\n",
           NIL);
    director_multiple_inheritance = 1;
    directorLanguage();
  }

  String *Swig_class_name(Node *n) {
    String *name;
    name = Copy(Getattr(n, "sym:name"));
    return name;
  }

  void PrintIncludeArg() {
    Printv(stdout, SWIG_LIB, SWIG_FILE_DELIMITER, ocaml_path, "\n", NIL);
  }

  /* ------------------------------------------------------------
   * main()
   * ------------------------------------------------------------ */

  virtual void main(int argc, char *argv[]) {
    int i;

    prefix = 0;

    SWIG_library_directory(ocaml_path);

    // Look for certain command line options
    for (i = 1; i < argc; i++) {
      if (argv[i]) {
        if (strcmp(argv[i], "-help") == 0) {
          fputs(usage, stdout);
        } else if (strcmp(argv[i], "-where") == 0) {
          PrintIncludeArg();
          Exit(EXIT_SUCCESS);
        } else if (strcmp(argv[i], "-prefix") == 0) {
          if (argv[i + 1]) {
            prefix = NewString(argv[i + 1]);
            Swig_mark_arg(i);
            Swig_mark_arg(i + 1);
            i++;
          } else {
            Swig_arg_error();
          }
        } else if (strcmp(argv[i], "-oldvarnames") == 0) {
          Swig_mark_arg(i);
          old_variable_names = true;
        }
      }
    }

    // If a prefix has been specified make sure it ends in a '_' (not actually used!)
    if (prefix) {
      const char *px = Char(prefix);
      if (px[Len(prefix) - 1] != '_')
        Printf(prefix, "_");
    } else
      prefix = NewString("swig_");

    // Add a symbol for this module

    Preprocessor_define("SWIGOCAML 1", 0);

    // Read in default typemaps
    SWIG_config_file("ocaml.i");
    allow_overloading();
  }

  /* Swig_director_declaration()
   *
   * Generate the full director class declaration, complete with base classes.
   * e.g. "class SwigDirector_myclass : public myclass, public Swig::Director {"
   *
   */

  String *Swig_director_declaration(Node *n) {
    String *classname = Swig_class_name(n);
    String *directorname = NewStringf("SwigDirector_%s", classname);
    String *base = Getattr(n, "classtype");
    String *declaration = Swig_class_declaration(n, directorname);
    Printf(declaration, " : public %s, public Swig::Director {\n", base);
    Delete(classname);
    Delete(directorname);
    return declaration;
  }

  void emitBanner(File *f) {
    Printf(f, "(* ----------------------------------------------------------------------------\n");
    Swig_banner_target_lang(f, " *");
    Printf(f, " * ---------------------------------------------------------------------------- *)\n\n");
  }

  /* ------------------------------------------------------------
   * top()
   *
   * Recognize the %module, and capture the module name.
   * Create the default enum cases.
   * Set up the named outputs:
   *
   *  init
   *  ml
   *  mli
   *  wrapper
   *  header
   *  runtime
   *  directors
   *  directors_h
   * ------------------------------------------------------------ */

  virtual int top(Node *n) {
    /* Set comparison with none for ConstructorToFunction */
    setSubclassInstanceCheck(NewString("caml_list_nth(args,0) != Val_unit"));

    /* check if directors are enabled for this module.  note: this
     * is a "master" switch, without which no director code will be
     * emitted.  %feature("director") statements are also required
     * to enable directors for individual classes or methods.
     *
     * use %module(directors="1") modulename at the start of the
     * interface file to enable director generation.
     */
    String *mod_docstring = NULL;
    {
      Node *module = Getattr(n, "module");
      if (module) {
        Node *options = Getattr(module, "options");
        if (options) {
          if (Getattr(options, "directors")) {
            allow_directors();
          }
          if (Getattr(options, "dirprot")) {
            allow_dirprot();
          }
          if (Getattr(options, "sizeof")) {
            generate_sizeof = 1;
          }
          mod_docstring = Getattr(options, "docstring");
        }
      }
    }

    /* Initialize all of the output files */
    String *outfile = Getattr(n, "outfile");

    f_begin = NewFile(outfile, "w", SWIG_output_files());
    if (!f_begin) {
      FileErrorDisplay(outfile);
      Exit(EXIT_FAILURE);
    }
    f_runtime = NewString("");
    f_init = NewString("");
    f_header = NewString("");
    f_wrappers = NewString("");
    f_directors = NewString("");
    f_directors_h = NewString("");
    f_enumtypes_type = NewString("");
    f_enumtypes_value = NewString("");
    init_func_def = NewString("");
    f_mlbody = NewString("");
    f_mlibody = NewString("");
    f_mltail = NewString("");
    f_mlitail = NewString("");
    f_class_ctors = NewString("");
    f_class_ctors_end = NewString("");
    f_enum_to_int = NewString("");
    f_int_to_enum = NewString("");
    f_classtemplate = NewString("");

    module = Getattr(n, "name");

    seen_constructors = NewHash();
    seen_enums = NewHash();
    seen_enumvalues = NewHash();

    /* Register file targets with the SWIG file handler */
    Swig_register_filebyname("init", init_func_def);
    Swig_register_filebyname("header", f_header);
    Swig_register_filebyname("wrapper", f_wrappers);
    Swig_register_filebyname("begin", f_begin);
    Swig_register_filebyname("runtime", f_runtime);
    Swig_register_filebyname("mli", f_mlibody);
    Swig_register_filebyname("ml", f_mlbody);
    Swig_register_filebyname("mlitail", f_mlitail);
    Swig_register_filebyname("mltail", f_mltail);
    Swig_register_filebyname("director", f_directors);
    Swig_register_filebyname("director_h", f_directors_h);
    Swig_register_filebyname("classtemplate", f_classtemplate);
    Swig_register_filebyname("class_ctors", f_class_ctors);

    if (old_variable_names) {
      Swig_name_register("set", "%n%v__set__");
      Swig_name_register("get", "%n%v__get__");
    }

    Swig_banner(f_begin);

    Swig_obligatory_macros(f_runtime, "OCAML");

    Printf(f_runtime, "#define SWIG_MODULE \"%s\"\n", module);
    /* Module name */
    Printf(f_mlbody, "let module_name = \"%s\"\n", module);
    Printf(f_mlibody, "val module_name : string\n");
    Printf(f_enum_to_int,
           "let enum_to_int x (v : c_obj) =\n"
           "   match v with\n"
           "     C_enum _y ->\n"
           "     (let y = _y in match (x : c_enum_type) with\n"
           "       `unknown -> "
           "         (match y with\n"
           "           `Int x -> (Swig.C_int x)\n"
           "           | _ -> raise (LabelNotFromThisEnum v))\n");

    Printf(f_int_to_enum,
           "let int_to_enum x y =\n"
           "    match (x : c_enum_type) with\n"
           "      `unknown -> C_enum (`Int y)\n");

    if (Swig_directors_enabled()) {
      Printf(f_runtime, "#define SWIG_DIRECTORS\n");
    }

    Printf(f_runtime, "\n");

    /* Produce the enum_to_int and int_to_enum functions */

    Printf(f_enumtypes_type,
           "open Swig\n"
           "type c_enum_type = [ \n  `unknown\n");
    Printf(f_enumtypes_value, "type c_enum_value = [ \n  `Int of int\n");
    String *mlfile = NewString("");
    String *mlifile = NewString("");

    Printv(mlfile, module, ".ml", NIL);
    Printv(mlifile, module, ".mli", NIL);

    String *mlfilen = NewStringf("%s%s", SWIG_output_directory(), mlfile);
    if ((f_mlout = NewFile(mlfilen, "w", SWIG_output_files())) == 0) {
      FileErrorDisplay(mlfilen);
      Exit(EXIT_FAILURE);
    }
    String *mlifilen = NewStringf("%s%s", SWIG_output_directory(), mlifile);
    if ((f_mliout = NewFile(mlifilen, "w", SWIG_output_files())) == 0) {
      FileErrorDisplay(mlifilen);
      Exit(EXIT_FAILURE);
    }
    emitBanner(f_mlout);
    emitBanner(f_mliout);

    Language::top(n);

    if (mod_docstring) {
      if (Len(mod_docstring)) {
        Printv(f_mliout, "(** ", mod_docstring, " *)\n", NIL);
      }
      Delete(mod_docstring);
      mod_docstring = NULL;
    }

    Printf(f_enum_to_int,
           ") | _ -> (C_int (get_int v))\n"
           "let _ = Callback.register \"%s_enum_to_int\" enum_to_int\n",
           module);
    Printf(f_mlibody, "val enum_to_int : c_enum_type -> c_obj -> Swig.c_obj\n");

    Printf(f_int_to_enum, "let _ = Callback.register \"%s_int_to_enum\" int_to_enum\n", module);
    Printf(f_mlibody, "val int_to_enum : c_enum_type -> int -> c_obj\n");
    Printf(f_init,
           "#define SWIG_init f_%s_init\n"
           "%s"
           "}\n",
           module,
           init_func_def);
    Printf(f_mlbody,
           "external f_init : unit -> unit = \"f_%s_init\" ;;\n"
           "let _ = f_init ()\n",
           module);
    Printf(f_enumtypes_type, "]\n");
    Printf(f_enumtypes_value,
           "]\n\n"
           "type c_obj = c_enum_value c_obj_t\n");

    if (Swig_directors_enabled()) {
      // Insert director runtime into the f_runtime file (make it occur before %header section)
      Swig_insert_file("director_common.swg", f_runtime);
      Swig_insert_file("director.swg", f_runtime);
    }

    SwigType_emit_type_table(f_runtime, f_wrappers);
    /* Close all of the files */
    Dump(f_runtime, f_begin);
    Dump(f_directors_h, f_header);
    Dump(f_header, f_begin);
    Dump(f_directors, f_wrappers);
    Dump(f_wrappers, f_begin);
    Wrapper_pretty_print(f_init, f_begin);
    Delete(f_header);
    Delete(f_wrappers);
    Delete(f_init);
    Delete(f_runtime);
    Delete(f_begin);

    Dump(f_enumtypes_type, f_mlout);
    Dump(f_enumtypes_value, f_mlout);
    Dump(f_mlbody, f_mlout);
    Dump(f_enum_to_int, f_mlout);
    Dump(f_int_to_enum, f_mlout);
    Delete(f_int_to_enum);
    Delete(f_enum_to_int);
    Dump(f_class_ctors, f_mlout);
    Dump(f_class_ctors_end, f_mlout);
    Dump(f_mltail, f_mlout);
    Delete(f_mlout);

    Dump(f_enumtypes_type, f_mliout);
    Dump(f_enumtypes_value, f_mliout);
    Dump(f_mlibody, f_mliout);
    Dump(f_mlitail, f_mliout);
    Delete(f_mliout);

    return SWIG_OK;
  }

  /* Produce an error for the given type */
  void throw_unhandled_ocaml_type_error(SwigType *d, const char *types) {
    Swig_warning(WARN_TYPEMAP_UNDEF, input_file, line_number, "Unable to handle type %s (%s).\n", SwigType_str(d, 0), types);
  }

  /* Return true iff T is a pointer type */
  int is_a_pointer(SwigType *t) {
    return SwigType_ispointer(SwigType_typedef_resolve_all(t));
  }

  /*
   * Delete one reference from a given type.
   */

  void oc_SwigType_del_reference(SwigType *t) {
    if (SwigType_isqualifier(t)) {
      SwigType_del_qualifier(t);
    }
    SwigType_del_reference(t);
  }

  void oc_SwigType_del_array(SwigType *t) {
    if (SwigType_isqualifier(t)) {
      SwigType_del_qualifier(t);
    }
    if (SwigType_isarray(t)) {
      SwigType_del_array(t);
    }
  }

  /*
   * Return true iff T is a reference type
   */

  int is_a_reference(SwigType *t) {
    return SwigType_isreference(SwigType_typedef_resolve_all(t));
  }

  int is_an_array(SwigType *t) {
    return SwigType_isarray(SwigType_typedef_resolve_all(t));
  }

  virtual int membervariableHandler(Node *n) {
    String *symname = Getattr(n, "sym:name");
    Language::membervariableHandler(n);

    String *mname = Swig_name_member(NSPACE_TODO, classname, symname);
    String *getname = Swig_name_get(NSPACE_TODO, mname);
    String *mangled_getname = mangleNameForCaml(getname);
    Delete(getname);

    if (!GetFlag(n, "feature:immutable")) {
      String *setname = Swig_name_set(NSPACE_TODO, mname);
      String *mangled_setname = mangleNameForCaml(setname);
      Delete(setname);
      Printf(f_class_ctors,
             "    \"[%s]\", (fun args -> "
             "if args = (C_list [ raw_ptr ]) then _%s args else _%s args) ;\n",
             symname,
             mangled_getname,
             mangled_setname);
      Delete(mangled_setname);
    } else {
      Printf(f_class_ctors,
             "    \"[%s]\", (fun args -> "
             "if args = (C_list [ raw_ptr ]) then _%s args else C_void) ;\n",
             symname,
             mangled_getname);
    }
    Delete(mangled_getname);
    Delete(mname);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * functionWrapper()
   * Create a function declaration and register it with the interpreter.
   * ------------------------------------------------------------ */

  virtual int functionWrapper(Node *n) {
    char *iname = GetChar(n, "sym:name");
    SwigType *returntype = Getattr(n, "type");
    String *return_type_normalized = normalizeTemplatedClassName(returntype);
    ParmList *l = Getattr(n, "parms");
    int director_method = 0;
    Parm *p;

    Wrapper *f = NewWrapper();
    String *proc_name = NewString("");
    String *target = NewString("");
    String *arg = NewString("");
    String *cleanup = NewString("");
    String *outarg = NewString("");
    String *build = NewString("");
    String *tm;
    int i = 0;
    int numargs;
    int numreq;
    int newobj = GetFlag(n, "feature:new");
    String *nodeType = Getattr(n, "nodeType");
    int destructor = (!Cmp(nodeType, "destructor"));
    String *overname = 0;
    bool isOverloaded = Getattr(n, "sym:overloaded") ? true : false;
    // For overloaded functions, only the dispatch function needs to be exposed in the ml and mli files.
    bool expose_func = !isOverloaded || !Getattr(n, "sym:nextSibling");

    // Make a wrapper name for this
    String *wname = Swig_name_wrapper(iname);
    if (isOverloaded) {
      overname = Getattr(n, "sym:overname");
    } else {
      if (!addSymbol(iname, n)) {
        DelWrapper(f);
        return SWIG_ERROR;
      }
    }
    if (overname) {
      Append(wname, overname);
    }
    /* Do this to disambiguate functions emitted from different modules */
    Append(wname, module);

    Setattr(n, "wrap:name", wname);

    // Build the name for Scheme.
    Printv(proc_name, "_", iname, NIL);
    String *mangled_name = mangleNameForCaml(proc_name);

    if (classmode && in_constructor && expose_func) {  // Emit constructor for object
      String *mangled_name_nounder = NewString((char *)(Char(mangled_name)) + 1);
      Printf(f_class_ctors_end, "let %s clst = _%s clst\n", mangled_name_nounder, mangled_name_nounder);
      Printf(f_mlibody, "val %s : c_obj -> c_obj\n", mangled_name_nounder);
      Delete(mangled_name_nounder);
    } else if (classmode && in_destructor) {
      Printf(f_class_ctors, "    \"~\", %s ;\n", mangled_name);
    } else if (classmode && !in_constructor && !in_destructor && !static_member_function && !Getattr(n, "membervariableHandler:sym:name") && expose_func) {
      String *opname = Copy(Getattr(n, "memberfunctionHandler:sym:name"));

      Replaceall(opname, "operator ", "");
      Printf(f_class_ctors, "    \"%s\", %s ;\n", opname, mangled_name);
      Delete(opname);
    }

    if (classmode && in_constructor) {
      Setattr(seen_constructors, mangled_name, "true");
    }
    // writing the function wrapper function
    Printv(f->def, "SWIGEXT value ", wname, " (", NIL);
    Printv(f->def, "value args", NIL);
    Printv(f->def, ")\n{", NIL);

    /* Define the scheme name in C. This define is used by several
       macros. */
    // Printv(f->def, "#define FUNC_NAME \"", mangled_name, "\"", NIL);

    // adds local variables
    Wrapper_add_local(f, "args", "CAMLparam1(args)");
    Wrapper_add_local(f, "ret", "CAMLlocal2(swig_result,rv)");
    returntype = SwigType_typedef_qualified(returntype);
    emit_parameter_variables(l, f);

    /* Attach the standard typemaps */
    emit_attach_parmmaps(l, f);
    Setattr(n, "wrap:parms", l);

    numargs = emit_num_arguments(l);
    numreq = emit_num_required(l);
    if (!isOverloaded) {
      if (numargs > 0) {
        if (numreq > 0) {
          Printf(f->code, "if (caml_list_length(args) < %d || caml_list_length(args) > %d) {\n", numreq, numargs);
        } else {
          Printf(f->code, "if (caml_list_length(args) > %d) {\n", numargs);
        }
        Printf(f->code, "caml_invalid_argument(\"Incorrect number of arguments passed to '%s'\");\n}\n", iname);
      } else {
        Printf(f->code, "if (caml_list_length(args) > 0) caml_invalid_argument(\"'%s' takes no arguments\");\n", iname);
      }
    }
    Printf(f->code, "swig_result = Val_unit;\n");

    // Now write code to extract the parameters (this is super ugly)

    for (i = 0, p = l; i < numargs; i++) {
      /* Skip ignored arguments */
      while (checkAttribute(p, "tmap:in:numinputs", "0")) {
        p = Getattr(p, "tmap:in:next");
      }

      SwigType *pt = Getattr(p, "type");
      String *ln = Getattr(p, "lname");
      pt = SwigType_typedef_qualified(pt);

      // Produce names of source and target
      Clear(target);
      Clear(arg);
      String *source = NewStringf("caml_list_nth(args,%d)", i);
      Printf(target, "%s", ln);
      Printv(arg, Getattr(p, "name"), NIL);

      if (i >= numreq) {
        Printf(f->code, "if (caml_list_length(args) > %d) {\n", i);
      }
      // Handle parameter types.
      if ((tm = Getattr(p, "tmap:in"))) {
        Replaceall(tm, "$input", source);
        Setattr(p, "emit:input", source);
        Printv(f->code, tm, "\n", NIL);
        p = Getattr(p, "tmap:in:next");
      } else {
        // no typemap found
        // check if typedef and resolve
        throw_unhandled_ocaml_type_error(pt, "in");
        p = nextSibling(p);
      }
      if (i >= numreq) {
        Printf(f->code, "}\n");
      }
      Delete(source);
    }

    /* Insert constraint checking code */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:check"))) {
        Printv(f->code, tm, "\n", NIL);
        p = Getattr(p, "tmap:check:next");
      } else {
        p = nextSibling(p);
      }
    }

    // Pass output arguments back to the caller.

    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:argout"))) {
        Replaceall(tm, "$arg", Getattr(p, "emit:input"));
        Replaceall(tm, "$input", Getattr(p, "emit:input"));
        Replaceall(tm, "$ntype", normalizeTemplatedClassName(Getattr(p, "type")));
        Printv(outarg, tm, "\n", NIL);
        p = Getattr(p, "tmap:argout:next");
      } else {
        p = nextSibling(p);
      }
    }

    // Free up any memory allocated for the arguments.

    /* Insert cleanup code */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:freearg"))) {
        Printv(cleanup, tm, "\n", NIL);
        p = Getattr(p, "tmap:freearg:next");
      } else {
        p = nextSibling(p);
      }
    }

    /* if the object is a director, and the method call originated from its
     * underlying ocaml object, resolve the call by going up the c++
     * inheritance chain.  otherwise try to resolve the method in ocaml.
     * without this check an infinite loop is set up between the director and
     * shadow class method calls.
     */

    // NOTE: this code should only be inserted if this class is the
    // base class of a director class.  however, in general we haven't
    // yet analyzed all classes derived from this one to see if they are
    // directors.  furthermore, this class may be used as the base of
    // a director class defined in a completely different module at a
    // later time, so this test must be included whether or not directorbase
    // is true.  we do skip this code if directors have not been enabled
    // at the command line to preserve source-level compatibility with
    // non-polymorphic swig.  also, if this wrapper is for a smart-pointer
    // method, there is no need to perform the test since the calling object
    // (the smart-pointer) and the director object (the "pointee") are
    // distinct.

    director_method = is_member_director(n) && !is_smart_pointer() && !destructor;
    if (director_method) {
      Wrapper_add_local(f, "director", "Swig::Director *director = 0");
      Printf(f->code, "director = dynamic_cast<Swig::Director *>(arg1);\n");
      Wrapper_add_local(f, "upcall", "bool upcall = false");
      Append(f->code, "upcall = (director);\n");
    }

    // Now write code to make the function call
    if (Swig_director_emit_dynamic_cast(n, f)) {
      /* Add protection */
      Append(f->code, "if (!darg) {\n");
      Append(f->code, "  SWIG_exception(SWIG_NullReferenceError, \"'self' is not a director\");\n");
      Append(f->code, "}\n");
    }
    String *actioncode = emit_action(n);

    if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
      Replaceall(tm, "$result", "rv");
      Replaceall(tm, "$ntype", return_type_normalized);
      Printv(f->code, tm, "\n", NIL);
    } else {
      throw_unhandled_ocaml_type_error(returntype, "out");
    }
    emit_return_variable(n, returntype, f);

    // Dump the argument output code
    Printv(f->code, Char(outarg), NIL);

    // Dump the argument cleanup code
    Printv(f->code, Char(cleanup), NIL);

    // Look for any remaining cleanup

    if (GetFlag(n, "feature:new")) {
      if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
        Printv(f->code, tm, "\n", NIL);
      }
    }

    /* See if there is any return cleanup code */
    if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
      Printf(f->code, "%s\n", tm);
      Delete(tm);
    }

    // Free any memory allocated by the function being wrapped..

    if ((tm = Swig_typemap_lookup("swig_result", n, Swig_cresult_name(), 0))) {
      Printv(f->code, tm, "\n", NIL);
    }
    // Wrap things up (in a manner of speaking)

    Printv(f->code, tab4, "swig_result = caml_list_append(swig_result,rv);\n", NIL);
    Printv(f->code, tab4, "CAMLreturn(swig_result);\n", NIL);
    Printv(f->code, "}\n", NIL);

    bool isvoid = !Cmp(returntype, "void");
    Replaceall(f->code, "$isvoid", isvoid ? "1" : "0");

    /* Substitute the function name */
    Replaceall(f->code, "$symname", iname);

    Wrapper_print(f, f_wrappers);

    if (isOverloaded) {
      if (!Getattr(n, "sym:nextSibling")) {
        int maxargs;
        bool check_emitted = false;
        Wrapper *df = NewWrapper();
        String *dispatch = Swig_overload_dispatch(n,
                                                  "free(argv);\n"
                                                  "CAMLreturn(%s(args));\n",
                                                  &maxargs,
                                                  &check_emitted);

        Wrapper_add_local(df, "argv", "value *argv");

        /* Undifferentiate name .. this is the dispatch function */
        wname = Swig_name_wrapper(iname);
        /* Do this to disambiguate functions emitted from different
         * modules */
        Append(wname, module);

        Printv(df->def,
               "SWIGEXT value ",
               wname,
               "(value args) {\n"
               "  CAMLparam1(args);\n"
               "  int i;\n"
               "  int argc = caml_list_length(args);\n",
               NIL);
        Printv(df->code,
               "argv = (value *)malloc( argc * sizeof( value ) );\n"
               "for( i = 0; i < argc; i++ ) {\n"
               "  argv[i] = caml_list_nth(args,i);\n"
               "}\n",
               NIL);
        Printv(df->code, dispatch, "\nfree(argv);\n", NIL);
        Node *sibl = n;
        while (Getattr(sibl, "sym:previousSibling"))
          sibl = Getattr(sibl, "sym:previousSibling");
        String *protoTypes = NewString("");
        do {
          String *fulldecl = Swig_name_decl(sibl);
          Printf(protoTypes, "\n\"    %s\\n\"", fulldecl);
          Delete(fulldecl);
        } while ((sibl = Getattr(sibl, "sym:nextSibling")));
        Printf(df->code,
               "caml_failwith(\"Wrong number or type of arguments for overloaded function '%s'.\\n\""
               "\n\"  Possible C/C++ prototypes are:\\n\"%s);\n",
               iname,
               protoTypes);
        Delete(protoTypes);
        Printv(df->code, "}\n", NIL);
        Wrapper_print(df, f_wrappers);

        DelWrapper(df);
        Delete(dispatch);
      }
    }

    if (expose_func) {
      Printf(f_mlbody, "external %s_f : c_obj list -> c_obj list = \"%s\" ;;\n", mangled_name, wname);
      Printf(f_mlbody,
             "let %s arg = match %s_f (%s(fnhelper arg)) with\n",
             mangled_name,
             mangled_name,
             in_constructor && Swig_directorclass(getCurrentClass()) ? "director_core_helper " : "");
      Printf(f_mlbody,
             "  [] -> C_void\n"
             "| [x] -> (if %s then Gc.finalise \n"
             "  (fun x -> ignore ((invoke x) \"~\" C_void)) x) ; x\n"
             "| lst -> C_list lst ;;\n",
             newobj ? "true" : "false");
    }

    if ((!classmode || in_constructor || in_destructor || static_member_function) && expose_func)
      Printf(f_mlibody, "val %s : c_obj -> c_obj\n", mangled_name);

    Delete(proc_name);
    Delete(target);
    Delete(arg);
    Delete(outarg);
    Delete(cleanup);
    Delete(build);
    DelWrapper(f);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * variableWrapper()
   *
   * Create a link to a C variable.
   * This creates a single function _wrap_varname().
   * This function takes a single optional argument.   If supplied, it means
   * we are setting this variable to some value.  If omitted, it means we are
   * simply evaluating this variable.  We return the value of the variable
   * in both cases.
   *
   * symname is the name of the variable with respect to C.  This
   * may need to differ from the original name in the case of enums.
   * enumvname is the name of the variable with respect to ocaml.  This
   * will vary if the variable has been renamed.
   * ------------------------------------------------------------ */

  virtual int variableWrapper(Node *n) {
    char *name = GetChar(n, "feature:symname");
    String *iname = Getattr(n, "feature:enumvname");
    String *mname = mangleNameForCaml(iname);
    SwigType *t = Getattr(n, "type");

    String *proc_name = NewString("");
    String *tm;
    Wrapper *f;

    if (!name) {
      name = GetChar(n, "name");
    }

    if (!iname) {
      iname = Getattr(n, "sym:name");
      mname = mangleNameForCaml(NewString(iname));
    }

    if (!iname || !addSymbol(iname, n))
      return SWIG_ERROR;

    f = NewWrapper();

    // evaluation function names
    String *var_name = Swig_name_wrapper(iname);

    // Build the name for OCaml.
    Printv(proc_name, iname, NIL);
    Setattr(n, "wrap:name", proc_name);

    Printf(f->def, "SWIGEXT value %s(value args) {\n", var_name);
    // Printv(f->def, "#define FUNC_NAME \"", proc_name, "\"", NIL);

    Wrapper_add_local(f, "args", "CAMLparam1(args)");
    Wrapper_add_local(f, "swig_result", "CAMLlocal1(swig_result)");
    Printf(f->code, "swig_result = Val_unit;\n");

    int assignable = !is_immutable(n);
    if (assignable) {
      /* Check for a setting of the variable value */
      Printf(f->code, "if (args != Val_int(0)) {\n");
      if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
        Replaceall(tm, "$input", "args");
        emit_action_code(n, f->code, tm);
      } else if ((tm = Swig_typemap_lookup("in", n, name, 0))) {
        Replaceall(tm, "$input", "args");
        emit_action_code(n, f->code, tm);
      } else {
        throw_unhandled_ocaml_type_error(t, "varin/in");
      }
      Printf(f->code, "}\n");
    }
    // Now return the value of the variable (regardless
    // of evaluating or setting)

    if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
      Replaceall(tm, "$result", "swig_result");
      emit_action_code(n, f->code, tm);
    } else if ((tm = Swig_typemap_lookup("out", n, name, 0))) {
      Replaceall(tm, "$result", "swig_result");
      emit_action_code(n, f->code, tm);
    } else {
      throw_unhandled_ocaml_type_error(t, "varout/out");
    }

    Printf(f->code, "\nCAMLreturn(swig_result);\n");
    Printf(f->code, "}\n");

    Wrapper_print(f, f_wrappers);

    // Now add symbol to the Ocaml interpreter

    if (!assignable) {
      Printf(f_mlbody, "external _%s : c_obj -> Swig.c_obj = \"%s\" \n", mname, var_name);
      Printf(f_mlibody, "val _%s : c_obj -> Swig.c_obj\n", iname);
      if (const_enum) {
        Printf(f_enum_to_int, " | `%s -> _%s C_void\n", mname, mname);
        Printf(f_int_to_enum, " if y = (get_int (_%s C_void)) then `%s else\n", mname, mname);
      }
    } else {
      Printf(f_mlbody, "external _%s : c_obj -> c_obj = \"%s\"\n", mname, var_name);
      Printf(f_mlibody, "external _%s : c_obj -> c_obj = \"%s\"\n", mname, var_name);
    }

    Delete(var_name);
    Delete(proc_name);
    DelWrapper(f);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * staticmemberfunctionHandler --
   * Overridden to set static_member_function
   * ------------------------------------------------------------ */

  virtual int staticmemberfunctionHandler(Node *n) {
    static_member_function = 1;
    Language::staticmemberfunctionHandler(n);
    static_member_function = 0;
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * constantWrapper()
   *
   * The one trick here is that we have to make sure we rename the
   * constant to something useful that doesn't collide with the
   * original if any exists.
   * ------------------------------------------------------------ */

  virtual int constantWrapper(Node *n) {
    String *name = Getattr(n, "feature:symname");
    SwigType *type = Getattr(n, "type");
    String *value = Getattr(n, "value");
    SwigType *qname = Getattr(n, "qualified:name");

    if (qname)
      value = qname;

    if (!name) {
      name = mangleNameForCaml(Getattr(n, "name"));
      Insert(name, 0, "_swig_wrap_");
      Setattr(n, "feature:symname", name);
    }
    // See if there's a typemap

    // Create variable and assign it a value
    Printf(f_header, "static %s = %s;\n", SwigType_str(type, name), value);
    SetFlag(n, "feature:immutable");
    variableWrapper(n);
    return SWIG_OK;
  }

  int constructorHandler(Node *n) {
    int ret;

    in_constructor = 1;
    ret = Language::constructorHandler(n);
    in_constructor = 0;

    return ret;
  }

  /* destructorHandler:
   * Turn on destructor flag to inform decisions in functionWrapper
   */

  int destructorHandler(Node *n) {
    int ret;

    in_destructor = 1;
    ret = Language::destructorHandler(n);
    in_destructor = 0;

    return ret;
  }

  /* copyconstructorHandler:
   * Turn on constructor and copyconstructor flags for functionWrapper
   */

  int copyconstructorHandler(Node *n) {
    int ret;

    in_copyconst = 1;
    in_constructor = 1;
    ret = Language::copyconstructorHandler(n);
    in_constructor = 0;
    in_copyconst = 0;

    return ret;
  }

  /*
   * A simple, somewhat general purpose function for writing to multiple
   * streams from a source template.  This allows the user to define the
   * class definition in ways different from the one I have here if they
   * want to.  It will also make the class definition system easier to
   * fiddle with when I want to change methods, etc.
   */

  void Multiwrite(String *s) {
    char *find_marker = strstr(Char(s), "(*Stream:");
    while (find_marker) {
      char *next = strstr(find_marker, "*)");
      find_marker += strlen("(*Stream:");

      if (next) {
        int num_chars = (int)(next - find_marker);
        String *stream_name = NewString(find_marker);
        Delslice(stream_name, num_chars, Len(stream_name));
        File *fout = Swig_filebyname(stream_name);
        if (fout) {
          next += strlen("*)");
          char *following = strstr(next, "(*Stream:");
          find_marker = following;
          if (!following)
            following = next + strlen(next);
          String *chunk = NewString(next);
          Delslice(chunk, (int)(following - next), Len(chunk));
          Printv(fout, chunk, NIL);
        }
      }
    }
  }

  bool isSimpleType(String *name) {
    char *ch = Char(name);

    return !(strchr(ch, '(') || strchr(ch, '<') || strchr(ch, ')') || strchr(ch, '>'));
  }

  /* We accept all chars in identifiers because we use strings to index
   * them. */
  int validIdentifier(String *name) {
    return Len(name) > 0 ? 1 : 0;
  }

  /* classHandler
   *
   * Create a "class" definition for ocaml.  I thought quite a bit about
   * how I should do this part of it, and arrived here, using a function
   * invocation to select a method, and dispatch.  This can obviously be
   * done better, but I can't see how, given that I want to support
   * overloaded methods, out parameters, and operators.
   *
   * I needed a system that would do this:
   *
   *  a Be able to call these methods:
   *   int foo( int x );
   *   float foo( int x, int &out );
   *
   *  b Be typeable, even in the presence of mutually dependent classes.
   *
   *  c Support some form of operator invocation.
   *
   * (c) I chose strings for the method names so that "+=" would be a
   * valid method name, and the somewhat natural << (invoke x) "+=" y >>
   * would work.
   *
   * (a) (b) Since the c_obj type exists, it's easy to return C_int in one
   * case and C_list [ C_float ; C_int ] in the other.  This makes tricky
   * problems with out parameters disappear; they're simply appended to the
   * return list.
   *
   * (b) Since every item that comes from C++ is the same type, there is no
   * problem with the following:
   *
   * class Foo;
   * class Bar { Foo *toFoo(); }
   * class Foo { Bar *toBar(); }
   *
   * Since the Objective caml types of Foo and Bar are the same.  Now that
   * I correctly incorporate SWIG's typechecking, this isn't a big deal.
   *
   * The class is in the form of a function returning a c_obj.  The c_obj
   * is a C_obj containing a function which invokes a method on the
   * underlying object given its type.
   *
   * The name emitted here is normalized before being sent to
   * Callback.register, because we need this string to look up properly
   * when the typemap passes the descriptor string.  I've been considering
   * some, possibly more forgiving method that would do some transformations
   * on the $descriptor in order to find a potential match.  This is for
   * later.
   *
   * Important things to note:
   *
   * We rely on exception handling (BadMethodName) in order to call an
   * ancestor.  This can be improved.
   *
   * The method used to get :classof could be improved to look at the type
   * info that the base pointer contains.  It's really an error to have a
   * SWIG-generated object that does not contain type info, since the
   * existence of the object means that SWIG knows the type.
   *
   * :parents could use :classof to tell what class it is and make a better
   * decision.  This could be nice, (i.e. provide a run-time graph of C++
   * classes represented);.
   *
   * I can't think of a more elegant way of converting a C_obj fun to a
   * pointer than "operator &"...
   *
   * Added a 'sizeof' that will allow you to do the expected thing.
   * This should help users to fill buffer structs and the like (as is
   * typical in windows-styled code).  It's only enabled if you give
   * %feature(sizeof) and then, only for simple types.
   *
   * Overall, carrying the list of methods and base classes has worked well.
   * It allows me to give the Ocaml user introspection over their objects.
   */

  int classHandler(Node *n) {
    String *name = Getattr(n, "name");
    classname = Getattr(n, "sym:name");

    if (!name)
      return SWIG_OK;

    String *mangled_name = mangleNameForCaml(name);
    String *this_class_def = NewString(f_classtemplate);
    String *name_normalized = normalizeTemplatedClassName(name);
    String *old_class_ctors = f_class_ctors;
    String *base_classes = NewString("");
    f_class_ctors = NewString("");
    bool sizeof_feature = generate_sizeof && isSimpleType(name);

    classmode = true;
    int rv = Language::classHandler(n);
    classmode = false;

    if (sizeof_feature) {
      Printf(f_wrappers,
             "SWIGEXT value _wrap_%s_sizeof( value args ) {\n"
             "    CAMLparam1(args);\n"
             "    CAMLreturn(Val_int(sizeof(%s)));\n"
             "}\n",
             mangled_name,
             name_normalized);

      Printf(f_mlbody,
             "external __%s_sizeof : unit -> int = "
             "\"_wrap_%s_sizeof\"\n",
             mangled_name,
             mangled_name);
    }

    /* Insert sizeof operator for concrete classes */
    if (sizeof_feature) {
      Printv(f_class_ctors, "\"sizeof\" , (fun args -> C_int (__", mangled_name, "_sizeof ())) ;\n", NIL);
    }
    /* Handle up-casts in a nice way */
    List *baselist = Getattr(n, "bases");
    if (baselist && Len(baselist)) {
      Iterator b;
      b = First(baselist);
      while (b.item) {
        String *bname = Getattr(b.item, "name");
        if (bname) {
          String *base_create = NewString("");
          Printv(base_create, "(create_class \"", bname, "\")", NIL);
          Printv(f_class_ctors, "   \"::", bname, "\", (fun args -> ", base_create, " args) ;\n", NIL);
          Printv(base_classes, base_create, " ;\n", NIL);
        }
        b = Next(b);
      }
    }

    Replaceall(this_class_def, "$classname", mangled_name);
    Replaceall(this_class_def, "$normalized", name_normalized);
    Replaceall(this_class_def, "$realname", name);
    Replaceall(this_class_def, "$baselist", base_classes);
    Replaceall(this_class_def, "$classbody", f_class_ctors);

    Delete(f_class_ctors);
    f_class_ctors = old_class_ctors;

    // Actually write out the class definition

    Multiwrite(this_class_def);

    Setattr(n, "ocaml:ctor", mangled_name);

    return rv;
  }

  String *normalizeTemplatedClassName(String *name) {
    String *name_normalized = SwigType_typedef_resolve_all(name);
    bool took_action;

    do {
      took_action = false;

      if (is_a_pointer(name_normalized)) {
        SwigType_del_pointer(name_normalized);
        took_action = true;
      }

      if (is_a_reference(name_normalized)) {
        oc_SwigType_del_reference(name_normalized);
        took_action = true;
      }

      if (is_an_array(name_normalized)) {
        oc_SwigType_del_array(name_normalized);
        took_action = true;
      }
    } while (took_action);

    return SwigType_str(name_normalized, 0);
  }

  /*
   * Produce the symbol name that ocaml will use when referring to the
   * target item.  I wonder if there's a better way to do this:
   * (WF - use Swig_name_mangle_string/Swig_name_mangle_type)
   *
   * I shudder to think about doing it with a hash lookup, but that would
   * make a couple of things easier:
   */

  String *mangleNameForCaml(String *s) {
    String *out = Copy(s);
    Replaceall(out, " ", "_xx");
    Replaceall(out, "::", "_xx");
    Replaceall(out, ",", "_x");
    Replaceall(out, "+", "_xx_plus");
    Replaceall(out, "-", "_xx_minus");
    Replaceall(out, "<", "_xx_ldbrace");
    Replaceall(out, ">", "_xx_rdbrace");
    Replaceall(out, "!", "_xx_not");
    Replaceall(out, "%", "_xx_mod");
    Replaceall(out, "^", "_xx_xor");
    Replaceall(out, "*", "_xx_star");
    Replaceall(out, "&", "_xx_amp");
    Replaceall(out, "|", "_xx_or");
    Replaceall(out, "(", "_xx_lparen");
    Replaceall(out, ")", "_xx_rparen");
    Replaceall(out, "[", "_xx_lbrace");
    Replaceall(out, "]", "_xx_rbrace");
    Replaceall(out, "~", "_xx_bnot");
    Replaceall(out, "=", "_xx_equals");
    Replaceall(out, "/", "_xx_slash");
    Replaceall(out, ".", "_xx_dot");
    Replaceall(out, "?", "_xx_question");
    Replaceall(out, ":", "_xx_colon");
    return out;
  }

  SwigType *fully_qualified_enum_type(Node *n) {
    Node *parent = 0;
    String *fully_qualified_name = NewString("");
    String *parent_type = 0;

    parent = parentNode(n);
    while (parent) {
      parent_type = nodeType(parent);
      if (Getattr(parent, "name")) {
        String *parent_copy = NewStringf("%s::", Getattr(parent, "name"));
        if (Cmp(parent_type, "class") == 0 || Cmp(parent_type, "namespace") == 0)
          Insert(fully_qualified_name, 0, parent_copy);
        Delete(parent_copy);
      }
      if (!Cmp(parent_type, "class"))
        break;
      parent = parentNode(parent);
    }

    return fully_qualified_name;
  }

  /* Benedikt Grundmann inspired --> Enum wrap styles */

  int enumvalueDeclaration(Node *n) {
    String *name = Getattr(n, "name");
    String *symname = Getattr(n, "sym:name");
    SwigType *qtype = 0;

    if (name_qualifier_type) {
      qtype = Copy(name_qualifier_type);
      Printv(qtype, name, NIL);
    }

    if (const_enum && qtype && symname && !Getattr(seen_enumvalues, symname)) {
      Setattr(seen_enumvalues, symname, "true");
      SetFlag(n, "feature:immutable");
      Setattr(n, "feature:enumvalue", "1");  // this does not appear to be used

      Setattr(n, "qualified:name", SwigType_namestr(qtype));

      String *evname = SwigType_manglestr(qtype);
      Insert(evname, 0, "SWIG_ENUM_");

      Setattr(n, "feature:enumvname", symname);
      Setattr(n, "feature:symname", evname);
      Delete(evname);
      Printf(f_enumtypes_value, "| `%s\n", symname);

      return Language::enumvalueDeclaration(n);
    } else
      return SWIG_OK;
  }

  /* -------------------------------------------------------------------
   * This function is a bit uglier than it deserves.
   *
   * I used to direct lookup the name of the enum.  Now that certain fixes
   * have been made in other places, the names of enums are now fully
   * qualified, which is a good thing, overall, but requires me to do
   * some legwork.
   *
   * The other thing that uglifies this function is the varying way that
   * typedef enum and enum are handled.  I need to produce consistent names,
   * which means looking up and registering by typedef and enum name. */
  int enumDeclaration(Node *n) {
    if (getCurrentClass() && (cplus_mode != PUBLIC))
      return SWIG_NOWRAP;

    String *name = Getattr(n, "name");
    if (name) {
      String *oname = NewString(name);
      /* name is now fully qualified */
      String *fully_qualified_name = NewString(name);
      bool seen_enum = false;
      if (name_qualifier_type)
        Delete(name_qualifier_type);
      char *strip_position;
      name_qualifier_type = fully_qualified_enum_type(n);

      strip_position = strstr(Char(oname), "::");

      while (strip_position) {
        strip_position += 2;
        oname = NewString(strip_position);
        strip_position = strstr(Char(oname), "::");
      }

      seen_enum = (Getattr(seen_enums, fully_qualified_name) ? true : false);

      if (!seen_enum) {
        const_enum = true;
        Printf(f_enum_to_int, "| `%s -> (match y with\n", oname);
        Printf(f_int_to_enum, "| `%s -> C_enum (\n", oname);
        /* * * * A note about enum name resolution * * * *
         * This code should now work, but I think we can do a bit better.
         * The problem I'm having is that swig isn't very precise about
         * typedef name resolution.  My opinion is that SwigType_typedef
         * resolve_all should *always* return the enum tag if one exists,
         * rather than the admittedly friendlier enclosing typedef.
         *
         * This would make one of the cases below unnecessary.
         * * * */
        Printf(f_mlbody, "let _ = Callback.register \"%s_marker\" (`%s)\n", fully_qualified_name, oname);
        if (!strncmp(Char(fully_qualified_name), "enum ", 5)) {
          String *fq_noenum = NewString(Char(fully_qualified_name) + 5);
          Printf(f_mlbody,
                 "let _ = Callback.register \"%s_marker\" (`%s)\n"
                 "let _ = Callback.register \"%s_marker\" (`%s)\n",
                 fq_noenum,
                 oname,
                 fq_noenum,
                 name);
        }

        Printf(f_enumtypes_type, "| `%s\n", oname);
        Insert(fully_qualified_name, 0, "enum ");
        Setattr(seen_enums, fully_qualified_name, n);
      }
    }

    int ret = Language::enumDeclaration(n);

    if (const_enum) {
      Printf(f_int_to_enum, "`Int y)\n");
      Printf(f_enum_to_int,
             "| `Int x -> Swig.C_int x\n"
             "| _ -> raise (LabelNotFromThisEnum v))\n");
    }

    const_enum = false;

    return ret;
  }

  /* ----------------------------------------------------------------------------
   * BEGIN C++ Director Class modifications
   * ------------------------------------------------------------------------- */

  /*
   * Modified polymorphism code for Ocaml language module.
   *
   * TODO
   *
   * Move some boilerplate code generation to Swig_...() functions.
   *
   */

  /* ---------------------------------------------------------------
   * classDirectorMethod()
   *
   * Emit a virtual director method to pass a method call on to the
   * underlying Python object.
   *
   * --------------------------------------------------------------- */

  int classDirectorMethod(Node *n, Node *parent, String *super) {
    int is_void = 0;
    int is_pointer = 0;
    String *storage = Getattr(n, "storage");
    String *value = Getattr(n, "value");
    String *decl = Getattr(n, "decl");
    SwigType *returntype = Getattr(n, "type");
    String *name = Getattr(n, "name");
    String *classname = Getattr(parent, "sym:name");
    String *c_classname = Getattr(parent, "name");
    String *symname = Getattr(n, "sym:name");
    String *declaration = NewString("");
    ParmList *l = Getattr(n, "parms");
    Wrapper *w = NewWrapper();
    String *tm;
    String *wrap_args = NewString("");
    int status = SWIG_OK;
    int idx;
    bool pure_virtual = false;
    bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;

    if (Cmp(storage, "virtual") == 0) {
      if (Cmp(value, "0") == 0) {
        pure_virtual = true;
      }
    }
    Printf(w->locals, "CAMLparam0();\n");

    /* determine if the method returns a pointer */
    is_pointer = SwigType_ispointer_return(decl);
    is_void = (!Cmp(returntype, "void") && !is_pointer);

    /* virtual method definition */
    String *target;
    String *pclassname = NewStringf("SwigDirector_%s", classname);
    String *qualified_name = NewStringf("%s::%s", pclassname, name);
    SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
    target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
    Printf(w->def, "%s", target);
    Delete(qualified_name);
    Delete(target);
    /* header declaration */
    target = Swig_method_decl(rtype, decl, name, l, 1);
    Printf(declaration, "    virtual %s", target);
    Delete(target);

    // Get any exception classes in the throws typemap
    if (Getattr(n, "noexcept")) {
      Append(w->def, " noexcept");
      Append(declaration, " noexcept");
    }
    ParmList *throw_parm_list = 0;
    if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
      Parm *p;
      int gencomma = 0;

      Append(w->def, " throw(");
      Append(declaration, " throw(");

      if (throw_parm_list)
        Swig_typemap_attach_parms("throws", throw_parm_list, 0);
      for (p = throw_parm_list; p; p = nextSibling(p)) {
        if (Getattr(p, "tmap:throws")) {
          if (gencomma++) {
            Append(w->def, ", ");
            Append(declaration, ", ");
          }
          String *str = SwigType_str(Getattr(p, "type"), 0);
          Append(w->def, str);
          Append(declaration, str);
          Delete(str);
        }
      }
      Append(w->def, ")");
      Append(declaration, ")");
    }
    Append(w->def, " {");
    Append(declaration, ";\n");
    /* declare method return value
     * if the return value is a reference or const reference, a specialized typemap must
     * handle it, including declaration of c_result ($result).
     */
    if (!is_void && (!ignored_method || pure_virtual)) {
      if (!SwigType_isclass(returntype)) {
        if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
          String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
          Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
          Delete(construct_result);
        } else {
          Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
        }
      } else {
        String *cres = SwigType_lstr(returntype, "c_result");
        Printf(w->code, "%s;\n", cres);
        Delete(cres);
      }
    }

    if (ignored_method) {
      if (!pure_virtual) {
        String *super_call = Swig_method_call(super, l);
        if (is_void)
          Printf(w->code, "%s;\n", super_call);
        else
          Printf(w->code, "CAMLreturnT(%s, %s);\n", SwigType_str(returntype, 0), super_call);
        Delete(super_call);
      } else {
        Printf(w->code,
               "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n",
               SwigType_namestr(c_classname),
               SwigType_namestr(name));
      }
    } else {
      Wrapper_add_local(w, "swig_result", "CAMLlocal2(swig_result, args)");
      /* attach typemaps to arguments (C/C++ -> Ocaml) */
      String *arglist = NewString("");

      Swig_director_parms_fixup(l);

      Swig_typemap_attach_parms("in", l, 0);
      Swig_typemap_attach_parms("directorin", l, w);
      Swig_typemap_attach_parms("directorargout", l, w);

      Parm *p;
      int num_arguments = emit_num_arguments(l);
      int i;
      char source[256];

      /* build argument list and type conversion string */
      for (i = 0, idx = 0, p = l; i < num_arguments; i++) {
        String *pname = Getattr(p, "name");
        String *ptype = Getattr(p, "type");

        Putc(',', arglist);
        if ((tm = Getattr(p, "tmap:directorin")) != 0) {
          Setattr(p, "emit:directorinput", pname);
          Replaceall(tm, "$input", pname);
          Replaceall(tm, "$owner", "0");
          if (Len(tm) == 0)
            Append(tm, pname);
          Printv(wrap_args, tm, "\n", NIL);
          p = Getattr(p, "tmap:directorin:next");
          continue;
        } else if (Cmp(ptype, "void")) {
          /* special handling for pointers to other C++ director classes.
           * ideally this would be left to a typemap, but there is currently no
           * way to selectively apply the dynamic_cast<> to classes that have
           * directors.  in other words, the type "SwigDirector_$1_lname" only exists
           * for classes with directors.  we avoid the problem here by checking
           * module.wrap::directormap, but it's not clear how to get a typemap to
           * do something similar.  perhaps a new default typemap (in addition
           * to SWIGTYPE) called DIRECTORTYPE?
           */
          if (SwigType_ispointer(ptype) || SwigType_isreference(ptype)) {
            Node *module = Getattr(parent, "module");
            Node *target = Swig_directormap(module, ptype);
            snprintf(source, sizeof(source), "obj%d", idx++);
            String *nonconst = 0;
            /* strip pointer/reference --- should move to Swig/stype.c */
            String *nptype = NewString(Char(ptype) + 2);
            /* name as pointer */
            String *ppname = Copy(pname);
            if (SwigType_isreference(ptype)) {
              Insert(ppname, 0, "&");
            }
            /* if necessary, cast away const since Python doesn't support it! */
            if (SwigType_isconst(nptype)) {
              nonconst = NewStringf("nc_tmp_%s", pname);
              String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(ptype, 0), ppname);
              Wrapper_add_localv(w, nonconst, SwigType_lstr(ptype, 0), nonconst, nonconst_i, NIL);
              Delete(nonconst_i);
              Swig_warning(WARN_LANG_DISCARD_CONST,
                           input_file,
                           line_number,
                           "Target language argument '%s' discards const in director method %s::%s.\n",
                           SwigType_str(ptype, pname),
                           SwigType_namestr(c_classname),
                           SwigType_namestr(name));
            } else {
              nonconst = Copy(ppname);
            }
            Delete(nptype);
            Delete(ppname);
            String *mangle = SwigType_manglestr(ptype);
            if (target) {
              String *director = NewStringf("director_%s", mangle);
              Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL);
              Wrapper_add_localv(w, source, "value", source, "= Val_unit", NIL);
              Printf(wrap_args, "%s = dynamic_cast<Swig::Director *>(%s);\n", director, nonconst);
              Printf(wrap_args, "if (!%s) {\n", director);
              Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
              Printf(wrap_args, "} else {\n");
              Printf(wrap_args, "%s = %s->swig_get_self();\n", source, director);
              Printf(wrap_args, "}\n");
              Delete(director);
              Printv(arglist, source, NIL);
            } else {
              Wrapper_add_localv(w, source, "value", source, "= Val_unit", NIL);
              Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
              // Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE_p_%s, 0);\n",
              //        source, nonconst, base);
              Printv(arglist, source, NIL);
            }
            Delete(mangle);
            Delete(nonconst);
          } else {
            Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF,
                         input_file,
                         line_number,
                         "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n",
                         SwigType_str(ptype, 0),
                         SwigType_namestr(c_classname),
                         SwigType_namestr(name));
            status = SWIG_NOWRAP;
            break;
          }
        }
        p = nextSibling(p);
      }

      Printv(w->code, "swig_result = Val_unit;\n", 0);
      Printf(w->code, "args = Val_unit;\n");

      /* wrap complex arguments to values */
      Printv(w->code, wrap_args, NIL);

      /* pass the method call on to the OCaml object */
      Printv(w->code,
             "swig_result = caml_swig_alloc(1,C_list);\n"
             "Store_field(swig_result,0,args);\n"
             "args = swig_result;\n"
             "swig_result = Val_unit;\n",
             0);
      Printf(w->code,
             "static const value *swig_ocaml_func_val = NULL;\n"
             "if (!swig_ocaml_func_val) {\n");
      Printf(w->code, "  swig_ocaml_func_val = caml_named_value(\"swig_runmethod\");\n  }\n");
      Printf(w->code, "swig_result = caml_callback3(*swig_ocaml_func_val,swig_get_self(),caml_copy_string(\"%s\"),args);\n", Getattr(n, "name"));
      /* exception handling */
      tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
      if (!tm) {
        tm = Getattr(n, "feature:director:except");
      }
      if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
        Printf(w->code, "if (!%s) {\n", Swig_cresult_name());
        Printf(w->code, "  value error = *caml_named_value(\"director_except\");\n");
        Replaceall(tm, "$error", "error");
        Printv(w->code, Str(tm), "\n", NIL);
        Printf(w->code, "}\n");
      }

      /*
       * Python method may return a simple object, or a tuple.
       * for in/out arguments, we have to extract the appropriate values from the
       * argument list, then marshal everything back to C/C++ (return value and
       * output arguments).
       */

      /* marshal return value and other outputs (if any) from value to C/C++
       * type */

      String *cleanup = NewString("");
      String *outarg = NewString("");

      tm = Swig_typemap_lookup("directorout", n, "c_result", w);
      if (tm != 0) {
        Replaceall(tm, "$input", "swig_result");
        /* TODO check this */
        if (Getattr(n, "wrap:disown")) {
          Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
        } else {
          Replaceall(tm, "$disown", "0");
        }
        Replaceall(tm, "$result", "c_result");
        Printv(w->code, tm, "\n", NIL);
      }

      /* marshal outputs */
      for (p = l; p;) {
        if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
          Replaceall(tm, "$result", "swig_result");
          Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
          Printv(w->code, tm, "\n", NIL);
          p = Getattr(p, "tmap:directorargout:next");
        } else {
          p = nextSibling(p);
        }
      }

      Delete(arglist);
      Delete(cleanup);
      Delete(outarg);
    }

    /* any existing helper functions to handle this? */
    if (!is_void) {
      if (!(ignored_method && !pure_virtual)) {
        String *rettype = SwigType_str(returntype, 0);
        if (!SwigType_isreference(returntype)) {
          Printf(w->code, "CAMLreturnT(%s, (%s)c_result);\n", rettype, rettype);
        } else {
          Printf(w->code, "CAMLreturnT(%s, (%s)*c_result);\n", rettype, rettype);
        }
        Delete(rettype);
      }
    } else {
      Printf(w->code, "CAMLreturn0;\n");
    }

    Printf(w->code, "}\n");

    // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
    String *inline_extra_method = NewString("");
    if (dirprot_mode() && !is_public(n) && !pure_virtual) {
      Printv(inline_extra_method, declaration, NIL);
      String *extra_method_name = NewStringf("%sSwigPublic", name);
      Replaceall(inline_extra_method, name, extra_method_name);
      Replaceall(inline_extra_method, ";\n", " {\n      ");
      if (!is_void)
        Printf(inline_extra_method, "return ");
      String *methodcall = Swig_method_call(super, l);
      Printv(inline_extra_method, methodcall, ";\n    }\n", NIL);
      Delete(methodcall);
      Delete(extra_method_name);
    }

    /* emit the director method */
    if (status == SWIG_OK) {
      Replaceall(w->code, "$isvoid", is_void ? "1" : "0");
      if (!Getattr(n, "defaultargs")) {
        Replaceall(w->code, "$symname", symname);
        Wrapper_print(w, f_directors);
        Printv(f_directors_h, declaration, NIL);
        Printv(f_directors_h, inline_extra_method, NIL);
      }
    }

    /* clean up */
    Delete(wrap_args);
    Delete(pclassname);
    DelWrapper(w);
    return status;
  }

  /* ------------------------------------------------------------
   * classDirectorConstructor()
   * ------------------------------------------------------------ */

  int classDirectorConstructor(Node *n) {
    Node *parent = Getattr(n, "parentNode");
    String *sub = NewString("");
    String *decl = Getattr(n, "decl");
    String *supername = Swig_class_name(parent);
    String *classname = NewString("");
    Printf(classname, "SwigDirector_%s", supername);

    /* insert self parameter */
    Parm *p, *q;
    ParmList *superparms = Getattr(n, "parms");
    ParmList *parms = CopyParmList(superparms);
    String *type = NewString("value");
    p = NewParm(type, NewString("self"), n);
    q = Copy(p);
    set_nextSibling(q, superparms);
    set_nextSibling(p, parms);
    parms = p;

    if (!Getattr(n, "defaultargs")) {
      /* constructor */
      {
        Wrapper *w = NewWrapper();
        String *call;
        String *basetype = Getattr(parent, "classtype");
        String *target = Swig_method_decl(0, decl, classname, parms, 0);
        call = Swig_csuperclass_call(0, basetype, superparms);
        Printf(w->def, "%s::%s: %s, Swig::Director(self) { }", classname, target, call);
        Delete(target);
        Wrapper_print(w, f_directors);
        Delete(call);
        DelWrapper(w);
      }

      /* constructor header */
      {
        String *target = Swig_method_decl(0, decl, classname, parms, 1);
        Printf(f_directors_h, "    %s;\n", target);
        Delete(target);
      }
    }

    Setattr(n, "parms", q);
    Language::classDirectorConstructor(n);

    Delete(sub);
    Delete(classname);
    Delete(supername);
    // Delete(parms);

    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * classDirectorDefaultConstructor()
   * ------------------------------------------------------------ */

  int classDirectorDefaultConstructor(Node *n) {
    String *classname;
    classname = Swig_class_name(n);

    /* insert self parameter */
    Parm *p, *q;
    ParmList *superparms = Getattr(n, "parms");
    ParmList *parms = CopyParmList(superparms);
    String *type = NewString("value");
    p = NewParm(type, NewString("self"), n);
    q = Copy(p);
    set_nextSibling(p, parms);

    {
      Wrapper *w = NewWrapper();
      Printf(w->def, "SwigDirector_%s::SwigDirector_%s(value self) : Swig::Director(self) { }", classname, classname);
      Wrapper_print(w, f_directors);
      DelWrapper(w);
    }
    Printf(f_directors_h, "    SwigDirector_%s(value self);\n", classname);
    Delete(classname);
    Setattr(n, "parms", q);
    return Language::classDirectorDefaultConstructor(n);
  }

  int classDirectorInit(Node *n) {
    String *declaration = Swig_director_declaration(n);
    Printf(f_directors_h,
           "\n"
           "%s\n"
           "public:\n",
           declaration);
    Delete(declaration);
    return Language::classDirectorInit(n);
  }

  int classDirectorEnd(Node *n) {
    Printf(f_directors_h, "};\n\n");
    return Language::classDirectorEnd(n);
  }

  /* ---------------------------------------------------------------------
   * typedefHandler
   *
   * This is here in order to maintain the correct association between
   * typedef names and enum names.
   *
   * Since I implement enums as polymorphic variant tags, I need to call
   * back into ocaml to evaluate them.  This requires a string that can
   * be generated in the typemaps, and also at SWIG time to be the same
   * string.  The problem that arises is that SWIG variously generates
   * enum e_name_tag
   * e_name_tag
   * e_typedef_name
   * for
   * typedef enum e_name_tag { ... } e_typedef_name;
   *
   * Since I need these strings to be consistent, I must maintain a correct
   * association list between typedef and enum names.
   * --------------------------------------------------------------------- */
  int typedefHandler(Node *n) {
    String *type = Getattr(n, "type");
    Node *enum_node = type ? Getattr(seen_enums, type) : 0;
    if (enum_node) {
      String *name = Getattr(enum_node, "name");

      Printf(f_mlbody, "let _ = Callback.register \"%s_marker\" (`%s)\n", Getattr(n, "name"), name);
    }
    return SWIG_OK;
  }

  String *runtimeCode() {
    String *s = Swig_include_sys("ocamlrun.swg");
    if (!s) {
      Printf(stderr, "*** Unable to open 'ocamlrun.swg'\n");
      s = NewString("");
    }
    return s;
  }

  String *defaultExternalRuntimeFilename() {
    return NewString("swigocamlrun.h");
  }
};

/* -------------------------------------------------------------------------
 * swig_ocaml()    - Instantiate module
 * ------------------------------------------------------------------------- */

static Language *new_swig_ocaml() {
  return new OCAML();
}
extern "C" Language *swig_ocaml(void) {
  return new_swig_ocaml();
}
