/* ----------------------------------------------------------------------------- 
 * 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 http://www.swig.org/legal.html.
 *
 * pike.cxx
 *
 * Pike language module for SWIG.
 * ----------------------------------------------------------------------------- */

/*
 * Notes:
 *
 * - The current approach used for "out" typemaps is inconsistent with
 *   how "out" typemaps are handled by other language modules. Instead
 *   of converting the C/C++ type ($1) to a Pike object type (e.g. a
 *   struct svalue), we're just calling the appropriate push_XXX
 *   (e.g. push_int) to push the return value onto the stack.
 *
 * - Pike classes can't have static member functions or data, so we need
 *   to find some other appropriate mapping for C++ static member functions
 *   and data.
 *
 * - Pike doesn't seem to provide any default way to print the memory
 *   address, etc. for extension objects. Should we do something here?
 *
 */

#include "swigmod.h"

#include <ctype.h>		// for isalnum()

static const char *usage = "\
Pike Options (available with -pike)\n\
     [no additional options]\n\
\n";

class PIKE:public Language {
private:

  File *f_begin;
  File *f_runtime;
  File *f_header;
  File *f_wrappers;
  File *f_init;
  File *f_classInit;

  String *PrefixPlusUnderscore;
  int current;

  // Wrap modes
  enum {
    NO_CPP,
    MEMBER_FUNC,
    CONSTRUCTOR,
    DESTRUCTOR,
    MEMBER_VAR,
    CLASS_CONST,
    STATIC_FUNC,
    STATIC_VAR
  };

public:

  /* ---------------------------------------------------------------------
   * PIKE()
   *
   * Initialize member data
   * --------------------------------------------------------------------- */

   PIKE() {
    f_begin = 0;
    f_runtime = 0;
    f_header = 0;
    f_wrappers = 0;
    f_init = 0;
    f_classInit = 0;
    PrefixPlusUnderscore = 0;
    current = NO_CPP;
  }

  /* ---------------------------------------------------------------------
   * main()
   *
   * Parse command line options and initializes variables.
   * --------------------------------------------------------------------- */
  
   virtual void main(int argc, char *argv[]) {

    /* Set location of SWIG library */
    SWIG_library_directory("pike");

    /* Look for certain command line options */
    for (int i = 1; i < argc; i++) {
      if (argv[i]) {
	if (strcmp(argv[i], "-help") == 0) {
	  fputs(usage, stdout);
	}
      }
    }

    /* Add a symbol to the parser for conditional compilation */
    Preprocessor_define("SWIGPIKE 1", 0);

    /* Set language-specific configuration file */
    SWIG_config_file("pike.swg");

    /* Set typemap language */
    SWIG_typemap_lang("pike");

    /* Enable overloaded methods support */
    allow_overloading();
  }

  /* ---------------------------------------------------------------------
   * top()
   * --------------------------------------------------------------------- */

  virtual int top(Node *n) {
    /* Get the module name */
    String *module = Getattr(n, "name");

    /* Get the output file name */
    String *outfile = Getattr(n, "outfile");

    /* Open the output file */
    f_begin = NewFile(outfile, "w", SWIG_output_files());
    if (!f_begin) {
      FileErrorDisplay(outfile);
      SWIG_exit(EXIT_FAILURE);
    }
    f_runtime = NewString("");
    f_init = NewString("");
    f_classInit = NewString("");
    f_header = NewString("");
    f_wrappers = NewString("");

    /* Register file targets with the SWIG file handler */
    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("init", f_init);
    Swig_register_filebyname("classInit", f_classInit);

    /* Standard stuff for the SWIG runtime section */
    Swig_banner(f_begin);

    Printf(f_runtime, "\n\n#ifndef SWIGPIKE\n#define SWIGPIKE\n#endif\n\n");

    Printf(f_header, "#define SWIG_init    pike_module_init\n");
    Printf(f_header, "#define SWIG_name    \"%s\"\n\n", module);

    /* Change naming scheme for constructors and destructors */
    Swig_name_register("construct", "%n%c_create");
    Swig_name_register("destroy", "%n%c_destroy");

    /* Current wrap type */
    current = NO_CPP;

    /* Emit code for children */
    Language::top(n);

    /* Close the initialization function */
    Printf(f_init, "}\n");
    SwigType_emit_type_table(f_runtime, f_wrappers);

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

    Delete(f_header);
    Delete(f_wrappers);
    Delete(f_init);
    Delete(f_classInit);
    Delete(f_runtime);
    Delete(f_begin);

    /* Done */
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * validIdentifier()
   * ------------------------------------------------------------ */

  virtual int validIdentifier(String *s) {
    char *c = Char(s);
    const char *c0 = c;
    const char *c1 = c0 + 1;
    while (*c) {
      if (*c == '`' && c == c0) {
	c++;
	continue;
      }
      if ((*c == '+' || *c == '-' || *c == '*' || *c == '/') && c == c1) {
	c++;
	continue;
      }
      if (!(isalnum(*c) || (*c == '_')))
	return 0;
      c++;
    }
    return 1;
  }

  /* ------------------------------------------------------------
   * importDirective()
   * ------------------------------------------------------------ */

  virtual int importDirective(Node *n) {
    String *modname = Getattr(n, "module");
    if (modname) {
      Printf(f_init, "pike_require(\"%s\");\n", modname);
    }
    return Language::importDirective(n);
  }

  /* ------------------------------------------------------------
   * strip()
   *
   * For names that begin with the current class prefix plus an
   * underscore (e.g. "Foo_enum_test"), return the base function
   * name (i.e. "enum_test").
   * ------------------------------------------------------------ */

  String *strip(const DOHconst_String_or_char_ptr name) {
    String *s = Copy(name);
    if (Strncmp(name, PrefixPlusUnderscore, Len(PrefixPlusUnderscore)) != 0) {
      return s;
    }
    Replaceall(s, PrefixPlusUnderscore, "");
    return s;
  }

  /* ------------------------------------------------------------
   * add_method()
   * ------------------------------------------------------------ */

  void add_method(const DOHconst_String_or_char_ptr name, const DOHconst_String_or_char_ptr function, const DOHconst_String_or_char_ptr description) {
    String *rename = NULL;
    switch (current) {
    case NO_CPP:
      rename = NewString(name);
      Printf(f_init, "ADD_FUNCTION(\"%s\", %s, tFunc(%s), 0);\n", rename, function, description);
      break;
    case STATIC_FUNC:
    case STATIC_VAR:
      rename = NewString(name);
      Printf(f_init, "ADD_FUNCTION(\"%s\", %s, tFunc(%s), 0);\n", rename, function, description);
      break;
    case CONSTRUCTOR:
    case DESTRUCTOR:
    case MEMBER_FUNC:
    case MEMBER_VAR:
      rename = strip(name);
      Printf(f_classInit, "ADD_FUNCTION(\"%s\", %s, tFunc(%s), 0);\n", rename, function, description);
      break;
    case CLASS_CONST:
      assert(false);		// shouldn't have gotten here for CLASS_CONST nodes
    default:
      assert(false);		// what is this?
    }
    Delete(rename);
  }

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

  virtual int functionWrapper(Node *n) {

    String *name = Getattr(n, "name");
    String *iname = Getattr(n, "sym:name");
    SwigType *d = Getattr(n, "type");
    ParmList *l = Getattr(n, "parms");

    Parm *p;
    String *tm;
    int i;

    String *overname = 0;
    if (Getattr(n, "sym:overloaded")) {
      overname = Getattr(n, "sym:overname");
    } else {
      if (!addSymbol(iname, n))
	return SWIG_ERROR;
    }

    Wrapper *f = NewWrapper();

    // Emit all of the local variables for holding arguments.
    emit_parameter_variables(l, f);

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

    /* Get number of required and total arguments */
    int num_arguments = emit_num_arguments(l);
    int varargs = emit_isvarargs(l);

    /* Which input argument to start with? */
    int start = (current == MEMBER_FUNC || current == MEMBER_VAR || current == DESTRUCTOR) ? 1 : 0;

    /* Offset to skip over the attribute name */
    // int offset = (current == MEMBER_VAR) ? 1 : 0;
    int offset = 0;

    String *wname = Swig_name_wrapper(iname);
    if (overname) {
      Append(wname, overname);
    }
    Setattr(n, "wrap:name", wname);

    Printv(f->def, "static void ", wname, "(INT32 args) {", NIL);

    /* Generate code for argument marshalling */
    String *description = NewString("");
    char source[64];
    for (i = 0, p = l; i < num_arguments; i++) {

      while (checkAttribute(p, "tmap:in:numinputs", "0")) {
	p = Getattr(p, "tmap:in:next");
      }

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

      if (i < start) {
	String *lstr = SwigType_lstr(pt, 0);
	Printf(f->code, "%s = (%s) THIS;\n", ln, lstr);
	Delete(lstr);
      } else {
	/* Look for an input typemap */
	sprintf(source, "Pike_sp[%d-args]", i - start + offset);
	if ((tm = Getattr(p, "tmap:in"))) {
	  Replaceall(tm, "$source", source);
	  Replaceall(tm, "$target", ln);
	  Replaceall(tm, "$input", source);
	  Setattr(p, "emit:input", source);
	  Printf(f->code, "%s\n", tm);
	  String *pikedesc = Getattr(p, "tmap:in:pikedesc");
	  if (pikedesc) {
	    Printv(description, pikedesc, " ", NIL);
	  }
	  p = Getattr(p, "tmap:in:next");
	  continue;
	} else {
	  Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
	  break;
	}
      }
      p = nextSibling(p);
    }

    /* Check for trailing varargs */
    if (varargs) {
      if (p && (tm = Getattr(p, "tmap:in"))) {
	Replaceall(tm, "$input", "varargs");
	Printv(f->code, tm, "\n", NIL);
      }
    }

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

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

    /* Insert argument output code */
    String *outarg = NewString("");
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:argout"))) {
	Replaceall(tm, "$source", Getattr(p, "lname"));
	Replaceall(tm, "$target", "resultobj");
	Replaceall(tm, "$arg", Getattr(p, "emit:input"));
	Replaceall(tm, "$input", Getattr(p, "emit:input"));
	Printv(outarg, tm, "\n", NIL);
	p = Getattr(p, "tmap:argout:next");
      } else {
	p = nextSibling(p);
      }
    }

    /* Emit the function call */
    String *actioncode = emit_action(n);

    /* Clear the return stack */
    Printf(actioncode, "pop_n_elems(args);\n");

    /* Return the function value */
    if (current == CONSTRUCTOR) {
      Printv(actioncode, "THIS = (void *) ", Swig_cresult_name(), ";\n", NIL);
      Printv(description, ", tVoid", NIL);
    } else if (current == DESTRUCTOR) {
      Printv(description, ", tVoid", NIL);
    } else {
      Printv(description, ", ", NIL);
      if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
        actioncode = 0;
	Replaceall(tm, "$source", Swig_cresult_name());
	Replaceall(tm, "$target", "resultobj");
	Replaceall(tm, "$result", "resultobj");
	if (GetFlag(n, "feature:new")) {
	  Replaceall(tm, "$owner", "1");
	} else {
	  Replaceall(tm, "$owner", "0");
	}
	String *pikedesc = Getattr(n, "tmap:out:pikedesc");
	if (pikedesc) {
	  Printv(description, pikedesc, NIL);
	}
	Printf(f->code, "%s\n", tm);
      } else {
	Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(d, 0), name);
      }
    }
    if (actioncode) {
      Append(f->code, actioncode);
      Delete(actioncode);
    }
    emit_return_variable(n, d, f);

    /* Output argument output code */
    Printv(f->code, outarg, NIL);

    /* Output cleanup code */
    Printv(f->code, cleanup, NIL);

    /* Look to see if there is any newfree cleanup code */
    if (GetFlag(n, "feature:new")) {
      if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
	Replaceall(tm, "$source", Swig_cresult_name());
	Printf(f->code, "%s\n", tm);
      }
    }

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

    /* Close the function */
    Printf(f->code, "}\n");

    /* Substitute the cleanup code */
    Replaceall(f->code, "$cleanup", cleanup);

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

    /* Dump the function out */
    Wrapper_print(f, f_wrappers);

    /* Now register the function with the interpreter. */
    if (!Getattr(n, "sym:overloaded")) {
      add_method(iname, wname, description);
    } else {
      if (!Getattr(n, "sym:nextSibling")) {
	dispatchFunction(n);
      }
    }

    Delete(cleanup);
    Delete(outarg);
    Delete(description);
    Delete(wname);
    DelWrapper(f);

    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * dispatchFunction()
   *
   * Emit overloading dispatch function
   * ------------------------------------------------------------ */

  void dispatchFunction(Node *n) {
    /* Last node in overloaded chain */

    int maxargs;
    String *tmp = NewString("");
    String *dispatch = Swig_overload_dispatch(n, "%s(args); return;", &maxargs);

    /* Generate a dispatch wrapper for all overloaded functions */

    Wrapper *f = NewWrapper();
    String *symname = Getattr(n, "sym:name");
    String *wname = Swig_name_wrapper(symname);

    Printf(f->def, "static void %s(INT32 args) {", wname);

    Wrapper_add_local(f, "argc", "INT32 argc");
    Printf(tmp, "struct svalue argv[%d]", maxargs);
    Wrapper_add_local(f, "argv", tmp);
    Wrapper_add_local(f, "ii", "INT32 ii");

    Printf(f->code, "argc = args;\n");
    Printf(f->code, "for (ii = 0; (ii < argc) && (ii < %d); ii++) {\n", maxargs);
    Printf(f->code, "argv[ii] = Pike_sp[ii-args];\n");
    Printf(f->code, "}\n");

    Replaceall(dispatch, "$args", "self, args");
    Printv(f->code, dispatch, "\n", NIL);
    Printf(f->code, "Pike_error(\"No matching function for overloaded '%s'.\");\n", symname);
    Printv(f->code, "}\n", NIL);

    Wrapper_print(f, f_wrappers);

    String *description = NewString("");
    Printf(description, "tAny,");
    if (current == CONSTRUCTOR || current == DESTRUCTOR) {
      Printf(description, " tVoid");
    } else {
      String *pd = Getattr(n, "tmap:out:pikedesc");
      if (pd)
	Printf(description, " %s", pd);
    }
    add_method(symname, wname, description);
    Delete(description);

    DelWrapper(f);
    Delete(dispatch);
    Delete(tmp);
    Delete(wname);
  }

  /* ------------------------------------------------------------
   * variableWrapper()
   * ------------------------------------------------------------ */

  virtual int variableWrapper(Node *n) {
    return Language::variableWrapper(n);
  }

  /* ------------------------------------------------------------
   * constantWrapper()
   * ------------------------------------------------------------ */

  virtual int constantWrapper(Node *n) {

    Swig_require("constantWrapper", n, "*sym:name", "type", "value", NIL);

    String *symname = Getattr(n, "sym:name");
    SwigType *type = Getattr(n, "type");
    String *value = Getattr(n, "value");
    bool is_enum_item = (Cmp(nodeType(n), "enumitem") == 0);

    if (SwigType_type(type) == T_MPOINTER) {
      /* Special hook for member pointer */
      String *wname = Swig_name_wrapper(symname);
      Printf(f_header, "static %s = %s;\n", SwigType_str(type, wname), value);
      value = wname;
    } else if (SwigType_type(type) == T_CHAR && is_enum_item) {
      type = NewSwigType(T_INT);
      Setattr(n, "type", type);
    }

    /* Perform constant typemap substitution */
    String *tm = Swig_typemap_lookup("constant", n, value, 0);
    if (tm) {
      Replaceall(tm, "$source", value);
      Replaceall(tm, "$target", symname);
      Replaceall(tm, "$symname", symname);
      Replaceall(tm, "$value", value);
      Printf(f_init, "%s\n", tm);
    } else {
      Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value %s = %s\n", SwigType_str(type, 0), value);
    }

    Swig_restore(n);

    return SWIG_OK;
  }

  /* ------------------------------------------------------------ 
   * nativeWrapper()
   * ------------------------------------------------------------ */

  virtual int nativeWrapper(Node *n) {
    //   return Language::nativeWrapper(n);
    String *name = Getattr(n, "sym:name");
    String *wrapname = Getattr(n, "wrap:name");

    if (!addSymbol(wrapname, n))
      return SWIG_ERROR;

    add_method(name, wrapname, 0);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * enumDeclaration()
   * ------------------------------------------------------------ */

  virtual int enumDeclaration(Node *n) {
    return Language::enumDeclaration(n);
  }

  /* ------------------------------------------------------------
   * enumvalueDeclaration()
   * ------------------------------------------------------------ */

  virtual int enumvalueDeclaration(Node *n) {
    return Language::enumvalueDeclaration(n);
  }

  /* ------------------------------------------------------------
   * classDeclaration()
   * ------------------------------------------------------------ */

  virtual int classDeclaration(Node *n) {
    return Language::classDeclaration(n);
  }

  /* ------------------------------------------------------------
   * classHandler()
   * ------------------------------------------------------------ */

  virtual int classHandler(Node *n) {

    String *symname = Getattr(n, "sym:name");
    if (!addSymbol(symname, n))
      return SWIG_ERROR;

    PrefixPlusUnderscore = NewStringf("%s_", getClassPrefix());

    Printf(f_classInit, "start_new_program();\n");

    /* Handle inheritance */
    List *baselist = Getattr(n, "bases");
    if (baselist && Len(baselist) > 0) {
      Iterator base = First(baselist);
      while (base.item) {
	String *basename = Getattr(base.item, "name");
	SwigType *basetype = NewString(basename);
	SwigType_add_pointer(basetype);
	SwigType_remember(basetype);
	String *basemangle = SwigType_manglestr(basetype);
	Printf(f_classInit, "low_inherit((struct program *) SWIGTYPE%s->clientdata, 0, 0, 0, 0, 0);\n", basemangle);
	Delete(basemangle);
	Delete(basetype);
	base = Next(base);
      }
    } else {
      Printf(f_classInit, "ADD_STORAGE(swig_object_wrapper);\n");
    }

    Language::classHandler(n);

    /* Accessors for member variables */
    /*
       List *membervariables = Getattr(n,"membervariables");
       if (membervariables && Len(membervariables) > 0) {
       membervariableAccessors(membervariables);
       }
     */

    /* Done, close the class and dump its definition to the init function */
    Printf(f_classInit, "add_program_constant(\"%s\", pr = end_program(), 0);\n", symname);
    Dump(f_classInit, f_init);
    Clear(f_classInit);

    SwigType *tt = NewString(symname);
    SwigType_add_pointer(tt);
    SwigType_remember(tt);
    String *tm = SwigType_manglestr(tt);
    Printf(f_init, "SWIG_TypeClientData(SWIGTYPE%s, (void *) pr);\n", tm);
    Delete(tm);
    Delete(tt);

    Delete(PrefixPlusUnderscore);
    PrefixPlusUnderscore = 0;

    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * memberfunctionHandler()
   *
   * Method for adding C++ member function
   * ------------------------------------------------------------ */

  virtual int memberfunctionHandler(Node *n) {
    current = MEMBER_FUNC;
    Language::memberfunctionHandler(n);
    current = NO_CPP;
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * constructorHandler()
   *
   * Method for adding C++ member constructor
   * ------------------------------------------------------------ */

  virtual int constructorHandler(Node *n) {
    current = CONSTRUCTOR;
    Language::constructorHandler(n);
    current = NO_CPP;
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * destructorHandler()
   * ------------------------------------------------------------ */

  virtual int destructorHandler(Node *n) {
    current = DESTRUCTOR;
    Language::destructorHandler(n);
    current = NO_CPP;
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * membervariableAccessors()
   * ------------------------------------------------------------ */

  void membervariableAccessors(List *membervariables) {
    String *name;
    Iterator i;
    bool need_setter;
    String *funcname;

    /* If at least one of them is mutable, we need a setter */
    need_setter = false;
    i = First(membervariables);
    while (i.item) {
      if (!GetFlag(i.item, "feature:immutable")) {
	need_setter = true;
	break;
      }
      i = Next(i);
    }

    /* Create a function to set the values of the (mutable) variables */
    if (need_setter) {
      Wrapper *wrapper = NewWrapper();
      String *setter = Swig_name_member(NSPACE_TODO, getClassPrefix(), "`->=");
      String *wname = Swig_name_wrapper(setter);
      Printv(wrapper->def, "static void ", wname, "(INT32 args) {", NIL);
      Printf(wrapper->locals, "char *name = (char *) STR0(Pike_sp[0-args].u.string);\n");

      i = First(membervariables);
      while (i.item) {
	if (!GetFlag(i.item, "feature:immutable")) {
	  name = Getattr(i.item, "name");
	  funcname = Swig_name_wrapper(Swig_name_set(NSPACE_TODO, Swig_name_member(NSPACE_TODO, getClassPrefix(), name)));
	  Printf(wrapper->code, "if (!strcmp(name, \"%s\")) {\n", name);
	  Printf(wrapper->code, "%s(args);\n", funcname);
	  Printf(wrapper->code, "return;\n");
	  Printf(wrapper->code, "}\n");
	  Delete(funcname);
	}
	i = Next(i);
      }

      /* Close the function */
      Printf(wrapper->code, "pop_n_elems(args);\n");
      Printf(wrapper->code, "}\n");

      /* Dump wrapper code to the output file */
      Wrapper_print(wrapper, f_wrappers);

      /* Register it with Pike */
      String *description = NewString("tStr tFloat, tVoid");
      add_method("`->=", wname, description);
      Delete(description);

      /* Clean up */
      Delete(wname);
      Delete(setter);
      DelWrapper(wrapper);
    }

    /* Create a function to get the values of the (mutable) variables */
    Wrapper *wrapper = NewWrapper();
    String *getter = Swig_name_member(NSPACE_TODO, getClassPrefix(), "`->");
    String *wname = Swig_name_wrapper(getter);
    Printv(wrapper->def, "static void ", wname, "(INT32 args) {", NIL);
    Printf(wrapper->locals, "char *name = (char *) STR0(Pike_sp[0-args].u.string);\n");

    i = First(membervariables);
    while (i.item) {
      name = Getattr(i.item, "name");
      funcname = Swig_name_wrapper(Swig_name_get(NSPACE_TODO, Swig_name_member(NSPACE_TODO, getClassPrefix(), name)));
      Printf(wrapper->code, "if (!strcmp(name, \"%s\")) {\n", name);
      Printf(wrapper->code, "%s(args);\n", funcname);
      Printf(wrapper->code, "return;\n");
      Printf(wrapper->code, "}\n");
      Delete(funcname);
      i = Next(i);
    }

    /* Close the function */
    Printf(wrapper->code, "pop_n_elems(args);\n");
    Printf(wrapper->code, "}\n");

    /* Dump wrapper code to the output file */
    Wrapper_print(wrapper, f_wrappers);

    /* Register it with Pike */
    String *description = NewString("tStr, tMix");
    add_method("`->", wname, description);
    Delete(description);

    /* Clean up */
    Delete(wname);
    Delete(getter);
    DelWrapper(wrapper);
  }

  /* ------------------------------------------------------------
   * membervariableHandler()
   * ------------------------------------------------------------ */

  virtual int membervariableHandler(Node *n) {
    List *membervariables = Getattr(getCurrentClass(), "membervariables");
    if (!membervariables) {
      membervariables = NewList();
      Setattr(getCurrentClass(), "membervariables", membervariables);
    }
    Append(membervariables, n);
    current = MEMBER_VAR;
    Language::membervariableHandler(n);
    current = NO_CPP;
    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------
   * staticmemberfunctionHandler()
   *
   * Wrap a static C++ function
   * ---------------------------------------------------------------------- */

  virtual int staticmemberfunctionHandler(Node *n) {
    current = STATIC_FUNC;
    Language::staticmemberfunctionHandler(n);
    current = NO_CPP;
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * memberconstantHandler()
   *
   * Create a C++ constant
   * ------------------------------------------------------------ */

  virtual int memberconstantHandler(Node *n) {
    current = CLASS_CONST;
    constantWrapper(n);
    current = NO_CPP;
    return SWIG_OK;
  }

  /* ---------------------------------------------------------------------
   * staticmembervariableHandler()
   * --------------------------------------------------------------------- */

  virtual int staticmembervariableHandler(Node *n) {
    current = STATIC_VAR;
    Language::staticmembervariableHandler(n);
    current = NO_CPP;
    return SWIG_OK;
  }
};

/* -----------------------------------------------------------------------------
 * swig_pike()    - Instantiate module
 * ----------------------------------------------------------------------------- */

static Language *new_swig_pike() {
  return new PIKE();
}
extern "C" Language *swig_pike(void) {
  return new_swig_pike();
}
