swig/Source/Modules1.1/ruby.cxx
2000-07-13 19:38:59 +00:00

1403 lines
42 KiB
C++

/********************************************************************
* Ruby module for SWIG
*
* $Header$
*
* Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
* Copyright (C) 2000 Information-technology Promotion Agency, Japan
*
* Masaki Fukushima
*
********************************************************************/
static char cvsroot[] = "$Header$";
#include "mod11.h"
#include "ruby.h"
#include <ctype.h>
#include <string.h>
static char *usage = (char*)"\
Ruby Options\n\
-module name - Set module name\n\
-toplevel - Do not define module\n\
-feature name - Set feature name (used by `require')\n";
static char *module;
static char *modvar;
static char *feature;
static int toplevel;
static DOH *other_extern = 0;
static DOH *other_init = 0;
static char *import_file;
static int current;
enum {
NO_CPP,
MEMBER_FUNC,
CONSTRUCTOR,
DESTRUCTOR,
MEMBER_VAR,
CLASS_CONST,
STATIC_FUNC,
STATIC_VAR
};
static DOHHash *classes; // key=cname val=RClass
static RClass *klass; // Currently processing class
static DOHHash *special_methods; // Python style special method name table
#define RCLASS(hash, name) (RClass*)(Getattr(hash, name) ? Data(Getattr(hash, name)) : 0)
#define SET_RCLASS(hash, name, klass) Setattr(hash, name, NewVoid(klass, 0))
static void indent(DOHString *s, char *sp = (char*)tab4) {
char rep[256] = "\n";
Insert(s,0,sp);
strcpy(rep+1,sp);
Replace(s,"\n", rep, DOH_REPLACE_ANY);
}
// ---------------------------------------------------------------------
// RUBY::RUBY()
//
// Constructor
// ---------------------------------------------------------------------
RUBY::RUBY(void) {
module = 0;
modvar = 0;
feature = 0;
toplevel = 0;
import_file = 0;
current = NO_CPP;
klass = 0;
classes = NewHash();
special_methods = NewHash();
// Python style special method name.
// Basic
Setattr(special_methods, "__repr__", "inspect");
Setattr(special_methods, "__str__", "to_s");
Setattr(special_methods, "__cmp__", "<=>");
Setattr(special_methods, "__hash__", "hash");
Setattr(special_methods, "__nonzero__", "nonzero?");
// Callable
Setattr(special_methods, "__call__", "call");
// Collection
Setattr(special_methods, "__len__", "length");
Setattr(special_methods, "__getitem__", "[]");
Setattr(special_methods, "__setitem__", "[]=");
// Numeric
Setattr(special_methods, "__add__", "+");
Setattr(special_methods, "__sub__", "-");
Setattr(special_methods, "__mul__", "*");
Setattr(special_methods, "__div__", "/");
Setattr(special_methods, "__mod__", "%");
Setattr(special_methods, "__divmod__", "divmod");
Setattr(special_methods, "__pow__", "**");
Setattr(special_methods, "__lshift__", "<<");
Setattr(special_methods, "__rshift__", ">>");
Setattr(special_methods, "__and__", "&");
Setattr(special_methods, "__xor__", "^");
Setattr(special_methods, "__or__", "|");
Setattr(special_methods, "__neg__", "-@");
Setattr(special_methods, "__pos__", "+@");
Setattr(special_methods, "__abs__", "abs");
Setattr(special_methods, "__invert__", "~");
Setattr(special_methods, "__int__", "to_i");
Setattr(special_methods, "__float__", "to_f");
Setattr(special_methods, "__coerce__", "coerce");
}
// ---------------------------------------------------------------------
// RUBY::parse_args(int argc, char *argv[])
//
// Parse command line options and initializes variables.
// ---------------------------------------------------------------------
void RUBY::parse_args(int argc, char *argv[]) {
// Look for certain command line options
for (int i = 1; i < argc; i++) {
if (argv[i]) {
if (strcmp(argv[i],"-module") == 0) {
if (argv[i+1]) {
set_module(argv[i+1],0);
Swig_mark_arg(i);
Swig_mark_arg(i+1);
i++;
} else {
Swig_arg_error();
}
} else if (strcmp(argv[i],"-feature") == 0) {
if (argv[i+1]) {
char *name = argv[i+1];
feature = new char [strlen(name)+1];
strcpy(feature, name);
Swig_mark_arg(i);
Swig_mark_arg(i+1);
i++;
} else {
Swig_arg_error();
}
} else if (strcmp(argv[i],"-toplevel") == 0) {
toplevel = 1;
Swig_mark_arg(i);
} else if (strcmp(argv[i],"-help") == 0) {
Printf(stderr,"%s\n", usage);
}
}
}
// Check consistency
if (module && toplevel)
Swig_arg_error();
// Set location of SWIG library
strcpy(LibDir,"ruby");
// Add a symbol to the parser for conditional compilation
Preprocessor_define((void *) "SWIGRUBY", 0);
// Add typemap definitions
typemap_lang = (char*)"ruby";
}
// ---------------------------------------------------------------------
// RUBY::parse()
//
// Start parsing an interface file.
// ---------------------------------------------------------------------
void RUBY::parse() {
printf("Generating wrappers for Ruby\n");
other_extern = NewString("");
other_init = NewString("");
headers();
// typedef void *VALUE
DataType *value = NewDataType(0);
strcpy(value->name, "void");
value->type = T_VOID;
value->is_pointer = 1;
value->implicit_ptr = 0;
DataType_typedef_add(value,(char*)"VALUE",0);
yyparse(); // Run the SWIG parser
DelDataType(value);
}
// ---------------------------------------------------------------------
// RUBY::set_module(char *mod_name,char **mod_list)
//
// Sets the module name. Does nothing if it's already set (so it can
// be overridden as a command line option).
//
// mod_list is a NULL-terminated list of additional modules. This
// is really only useful when building static executables.
//----------------------------------------------------------------------
void RUBY::set_module(char *mod_name, char **mod_list) {
if (import_file) {
Printf(f_init, "%srb_f_require(Qnil, rb_str_new2(\"%s\"));\n", tab4, mod_name);
free(import_file); // Note: was allocated from C
import_file = 0;
}
if (module) return;
if (!feature) {
feature = new char[strlen(mod_name)+1];
strcpy(feature, mod_name);
}
module = new char[strlen(mod_name)+1];
strcpy(module, mod_name);
// module name must be a constant.
module[0] = toupper(module[0]);
modvar = new char[1+strlen(module)+1];
modvar[0] = 'm';
strcpy(modvar+1, module);
if (mod_list) {
Printv(other_extern,
"#ifdef __cplusplus\n",
"extern \"C\" {\n",
"#endif\n",
0);
for (int i = 0; mod_list[i] != NULL; i++) {
Printv(other_extern, "extern void Init_", mod_list[i], "(void);\n", 0);
Printv(other_init, tab4, "Init_", mod_list[i], "();\n", 0);
}
Printv(other_extern,
"#ifdef __cplusplus\n",
"}\n",
"#endif\n",
0);
}
}
static void insert_file(char *filename, FILE *file) {
if (Swig_insert_file(filename, file) == -1) {
Printf(stderr,
"SWIG : Fatal error. "
"Unable to locate %s. (Possible installation problem).\n",
filename);
SWIG_exit(1);
}
}
// ----------------------------------------------------------------------
// RUBY::headers(void)
//
// Generate the appropriate header files for RUBY interface.
// ----------------------------------------------------------------------
void RUBY::headers(void) {
Swig_banner(f_header);
Printf(f_header,"/* Implementation : RUBY */\n\n");
Printf(f_header,"#define SWIGRUBY\n");
insert_file((char*)"ruby.swg", f_header);
if (NoInclude) {
insert_file((char*)"rubydec.swg", f_header);
} else {
insert_file((char*)"rubydef.swg", f_header);
}
}
// ---------------------------------------------------------------------
// RUBY::initialize(void)
//
// Produces an initialization function. Assumes that the module
// name has already been specified.
// ---------------------------------------------------------------------
void RUBY::initialize() {
if (!module) {
Printf(stderr,"SWIG : *** Warning. No module name specified.\n");
set_module((char*)"swig", NULL); // Pick a default name
}
Printf(f_header,"#define SWIG_init Init_%s\n", feature);
Printf(f_header,"#define SWIG_name \"%s\"\n\n", module);
if (!toplevel) Printf(f_header,"static VALUE %s;\n", modvar);
Printf(f_header,"\n%s\n", other_extern);
// Start generating the initialization function
Printv(f_init,
"\n",
"#ifdef __cplusplus\n",
"extern \"C\"\n",
"#endif\n",
"void Init_", feature, "(void) {\n",
other_init,
0);
if (!toplevel) {
Printv(f_init, tab4, modvar, " = rb_define_module(\"", module, "\");\n", 0);
}
Printf(f_init,"\n");
klass = new RClass();
}
// ---------------------------------------------------------------------
// RUBY::close(void)
//
// Finish the initialization function. Close any additional files and
// resources in use.
// ---------------------------------------------------------------------
void RUBY::close(void) {
// Finish off our init function
Printf(f_init, "\n");
emit_ptr_equivalence(f_wrappers,f_init);
Printf(f_init,"}\n");
}
// ---------------------------------------------------------------------
// RUBY::make_wrapper_name(char *cname)
//
// Creates a name for a wrapper function
// cname = Name of the C function
// ---------------------------------------------------------------------
char *RUBY::make_wrapper_name(char *cname) {
return Swig_name_wrapper(cname);
}
// ---------------------------------------------------------------------
// RUBY::create_command(char *cname, char *iname)
//
// Creates a new command from a C function.
// cname = Name of the C function
// iname = Name of function in scripting language
// argc = Number of arguments
// ---------------------------------------------------------------------
void RUBY::create_command(char *cname, char *iname, int argc) {
DOHString *wname = NewString(make_wrapper_name(cname));
if (CPlusPlus) {
Insert(wname,0,"VALUEFUNC(");
Append(wname,")");
}
if (current != NO_CPP)
iname = klass->strip(iname);
if (Getattr(special_methods, iname)) {
iname = GetChar(special_methods, iname);
}
DOHString *s = NewString("");
DOHString *temp = NewString("");
char argcs[32];
sprintf(argcs,"%d",argc);
switch (current) {
case MEMBER_FUNC:
Printv(klass->init, tab4, "rb_define_method(", klass->vname, ", \"",
iname, "\", ", wname, ", ", argcs, ");\n", 0);
break;
case CONSTRUCTOR:
Printv(s, tab4, "rb_define_singleton_method(", klass->vname,
", \"new\", ", wname, ", ", argcs, ");\n", 0);
Replace(klass->init,"$constructor", s, DOH_REPLACE_ANY);
break;
case MEMBER_VAR:
Append(temp,iname);
Replace(temp,"_set", "=", DOH_REPLACE_ANY);
Replace(temp,"_get", "", DOH_REPLACE_ANY);
Printv(klass->init, tab4, "rb_define_method(", klass->vname, ", \"",
temp, "\", ", wname, ", ", argcs, ");\n", 0);
break;
case STATIC_FUNC:
Printv(klass->init, tab4, "rb_define_singleton_method(", klass->vname,
", \"", iname, "\", ", wname, ", ", argcs, ");\n", 0);
break;
default:
if (toplevel) {
Printv(s, tab4, "rb_define_global_function(\"",
iname, "\", ", wname, ", ", argcs, ");\n",0);
} else {
Printv(s, tab4, "rb_define_module_function(", modvar, ", \"",
iname, "\", ", wname, ", ", argcs, ");\n",0);
}
Printv(f_init,s,0);
break;
}
Delete(s);
Delete(temp);
}
// ---------------------------------------------------------------------
// RUBY::create_function(char *name, char *iname, DataType *d, ParmList *l)
//
// Create a function declaration and register it with the interpreter.
// name = Name of real C function
// iname = Name of function in scripting language
// t = Return datatype
// l = Function parameters
// ---------------------------------------------------------------------
void RUBY::create_function(char *name, char *iname, DataType *t, ParmList *l) {
char source[256], target[256];
char *tm;
DOHString *cleanup, *outarg;
Wrapper *f;
int i;
// Ruby needs no destructor wrapper
if (current == DESTRUCTOR)
return;
char *wname = make_wrapper_name(name);
char mname[256], inamebuf[256];
int predicate = 0;
cleanup = NewString("");
outarg = NewString("");
f = NewWrapper();
switch (current) {
case MEMBER_FUNC:
case MEMBER_VAR:
case STATIC_FUNC:
strcpy(mname, klass->strip(iname));
if (Getattr(klass->predmethods, mname)) {
predicate = 1;
sprintf(inamebuf,"%s?",iname);
iname = inamebuf;
}
break;
}
// Get number of arguments
int numarg = ParmList_numarg(l);
int numopt = check_numopt(l);
int numignore = l->nparms - numarg;
int start = 0;
int use_self = 0;
switch (current) {
case MEMBER_FUNC:
case MEMBER_VAR:
numarg--;
start++;
use_self = 1;
break;
}
int numreq = 0;
int numoptreal = 0;
for (i = start; i < l->nparms; i++) {
if (!ParmList_get(l,i)->ignore) {
if (i >= l->nparms - numopt) numoptreal++;
else numreq++;
}
}
int vararg = (numoptreal != 0);
// Now write the wrapper function itself
Printv(f->def, "static VALUE\n", wname, "(", 0);
if (vararg) {
Printv(f->def, "int argc, VALUE *argv, VALUE self",0);
} else {
Printv(f->def, "VALUE self", 0);
for (i = start; i < l->nparms; i++) {
if (!ParmList_get(l,i)->ignore) {
Printf(f->def,", VALUE varg%d", i);
}
}
}
Printf(f->def,") {");
// Emit all of the local variables for holding arguments.
if (vararg) {
for (i = start; i < l->nparms; i++) {
if (!ParmList_get(l,i)->ignore) {
char s[256];
sprintf(s,"varg%d",i);
Wrapper_add_localv(f,s,"VALUE",s,0);
}
}
}
Wrapper_add_local(f,"vresult","VALUE vresult = Qnil");
int pcount = emit_args(t,l,f);
#if 0
// Dave's note: This won't work with strings gone -- 7/9/00 */
String temp;
temp << "/* " << name << " */" << br;
temp << "/* nparms = " << l->nparms << " */" << br;
temp << "/* numarg = " << numarg << " */" << br;
temp << "/* numopt = " << numopt << " */" << br;
temp << "/* numignore = " << numignore << " */" << br;
temp << "/* numoptreal = " << numoptreal << " */" << br;
temp << "/* pcount = " << pcount << " */" << br;
temp << "/* rb_scan_args(argc, argv, \""
<< (numarg-numoptreal) << numoptreal << "\") */" << br;
temp << br;
if (numignore != 0) Printf(stderr, temp);
f->code << temp;
#endif
// Emit count to check the number of arguments
if (vararg) {
Printf(f->code," rb_scan_args(argc, argv, \"%d%d\"", (numarg-numoptreal), numoptreal);
for (i = start; i < l->nparms; i++) {
if (!ParmList_get(l,i)->ignore) {
Printf(f->code,", &varg%d", i);
}
}
Printf(f->code,");\n");
}
// Now walk the function parameter list and generate code
// to get arguments
int j = 0; // Total number of non-optional arguments
for (i = 0; i < pcount ; i++) {
Parm *p = ParmList_get(l,i);
DataType *pt = Parm_Gettype(p);
char *pn = Parm_Getname(p);
// Produce string representation of source and target arguments
int selfp = (use_self && i == 0);
if (selfp)
strcpy(source,"self");
else
sprintf(source,"varg%d",i);
sprintf(target,"_arg%d",i);
if (!p->ignore) {
char *tab = (char*)tab4;
if (j >= (pcount-numopt)) { // Check if parsing an optional argument
Printf(f->code," if (argc > %d) {\n", j - start);
tab = (char*)tab8;
}
// Get typemap for this argument
tm = ruby_typemap_lookup((char*)"in",pt,pn,source,target,f);
if (tm) {
DOHString *s = NewString(tm);
indent(s,tab);
Printv(f->code, s, "\n", 0);
Replace(f->code, "$arg", source, DOH_REPLACE_ANY);
Delete(s);
} else {
Printf(stderr,"%s : Line %d. No typemapping for datatype %s\n",
input_file,line_number, DataType_print_type(pt));
}
if (j >= (pcount-numopt))
Printv(f->code, tab4, "} \n");
j++;
}
// Check to see if there was any sort of a constaint typemap
tm = ruby_typemap_lookup((char*)"check",pt,pn,source,target);
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(f->code, s, "\n", 0);
Replace(f->code, "$arg", source, DOH_REPLACE_ANY);
Delete(s);
}
// Check if there was any cleanup code (save it for later)
tm = ruby_typemap_lookup((char*)"freearg",pt,pn,target,source);
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(cleanup,s,"\n",0);
Replace(cleanup,"$arg",source, DOH_REPLACE_ANY);
Delete(s);
}
tm = ruby_typemap_lookup((char*)"argout",pt,pn,target,(char*)"vresult");
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(outarg, s, "\n", 0);
Replace(outarg, "$arg", source, DOH_REPLACE_ANY);
Delete(s);
}
}
// Now write code to make the function call
emit_func_call(name,t,l,f);
// Return value if necessary
if ((t->type != T_VOID) || (t->is_pointer)) {
if (predicate) {
Printv(f->code, tab4, "vresult = (_result ? Qtrue : Qfalse);\n", 0);
} else {
tm = ruby_typemap_lookup((char*)"out",t,name,(char*)"_result",(char*)"vresult");
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(f->code, s, "\n", 0);
Delete(s);
} else {
Printf(stderr,"%s : Line %d. No return typemap for datatype %s\n",
input_file,line_number,DataType_print_type(t));
}
}
}
// Dump argument output code;
Printv(f->code,outarg,0);
// Dump the argument cleanup code
Printv(f->code,cleanup,0);
// Look for any remaining cleanup. This processes the %new directive
if (NewObject) {
tm = ruby_typemap_lookup((char*)"newfree",t,name,(char*)"_result",(char*)"");
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(f->code,s,"\n", 0);
Delete(s);
}
}
// free pragma
if (current == MEMBER_FUNC && Getattr(klass->freemethods, mname)) {
Printv(f->code, tab4, "DATA_PTR(self) = 0;\n", 0);
}
// Special processing on return value.
tm = ruby_typemap_lookup((char*)"ret",t,name,(char*)"_result",(char*)"");
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(f->code,s,"\n", 0);
}
// Wrap things up (in a manner of speaking)
Printv(f->code, tab4, "return vresult;\n}\n", 0);
// Substitute the cleanup code
Replace(f->code,"$cleanup",cleanup, DOH_REPLACE_ANY);
// Emit the function
Wrapper_print(f,f_wrappers);
// Now register the function with the language
create_command(name, iname, (vararg ? -1 : numarg));
Delete(cleanup);
Delete(outarg);
DelWrapper(f);
}
// ---------------------------------------------------------------------
// RUBY::link_variable(char *name, char *iname, DataType *t)
//
// Create a link to a C variable.
// name = Name of C variable
// iname = Name of variable in scripting language
// t = Datatype of the variable
// ---------------------------------------------------------------------
void RUBY::link_variable(char *name, char *iname, DataType *t) {
char *tm;
DOHString *getfname, *setfname;
Wrapper *getf, *setf;
int mod_attr = 0;
getf = NewWrapper();
setf = NewWrapper();
// module attribute?
if (current == STATIC_VAR || !toplevel)
mod_attr = 1;
// create getter
getfname = NewString(Swig_name_get(name));
Replace(getfname,"::", "_", DOH_REPLACE_ANY); /* FIXME: Swig_name_get bug? */
Printv(getf->def, "static VALUE\n", getfname, "(", 0);
if (mod_attr) Printf(getf->def, "VALUE self");
Printf(getf->def, ") {");
Wrapper_add_local(getf,"_val","VALUE _val");
tm = ruby_typemap_lookup((char*)"varout",t,name,name,(char*)"_val");
if (!tm)
tm = ruby_typemap_lookup((char*)"out",t,name,name,(char*)"_val");
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(getf->code,s,"\n", 0);
Delete(s);
} else if (!t->is_pointer && t->type == T_USER) {
// Hack this into a pointer
t->is_pointer++;
DataType_remember(t);
Printv(getf->code, tab4, "_val = SWIG_NewPointerObj((void *)&", name,
", \"", DataType_print_mangle(t), "\");\n", 0);
t->is_pointer--;
} else {
Printf(stderr,"%s: Line %d. Unable to link with variable type %s\n",
input_file,line_number,DataType_print_type(t));
}
Printv(getf->code, tab4, "return _val;\n}\n", 0);
Wrapper_print(getf,f_wrappers);
if (Status & STAT_READONLY) {
setfname = NewString("NULL");
} else {
// create setter
setfname = NewString(Swig_name_set(name));
Replace(setfname,"::", "_", DOH_REPLACE_ANY); /* FIXME: Swig_name_get bug? */
if (mod_attr)
Printv(setf->def, "static VALUE\n", setfname, "(VALUE self, ", 0);
else
Printv(setf->def, "static void\n", setfname, "(", 0);
Printf(setf->def, "VALUE _val) {");
tm = ruby_typemap_lookup((char*)"varin",t,name,(char*)"_val",name);
if (!tm)
tm = ruby_typemap_lookup((char*)"in",t,name,(char*)"_val",name);
if (tm) {
DOHString *s = NewString(tm);
indent(s);
Printv(setf->code,s,"\n",0);
Delete(s);
} else if (!t->is_pointer && t->type == T_USER) {
t->is_pointer++;
Wrapper_add_localv(setf,"temp",DataType_print_type(t), "temp",0);
Printv(setf->code, tab4, "temp = (", DataType_print_type(t), ")",
"SWIG_ConvertPtr(_val, \"", DataType_print_mangle(t), "\");\n",
0);
Printv(setf->code, tab4, name, " = *temp;\n",0);
t->is_pointer--;
} else {
Printf(stderr,"%s: Line %d. Unable to link with variable type %s\n",
input_file,line_number,DataType_print_type(t));
}
if (mod_attr)
Printv(setf->code, tab4, "return _val;\n",0);
Printf(setf->code,"}\n");
Wrapper_print(setf,f_wrappers);
}
// define accessor method
if (CPlusPlus) {
Insert(getfname,0,"VALUEFUNC(");
Append(getfname,")");
Insert(setfname,0,"VALUEFUNC(");
Append(setfname,")");
}
DOHString *s = NewString("");
switch (current) {
case STATIC_VAR:
// C++ class variable
Printv(s,
tab4, "rb_define_singleton_method(", klass->vname, ", \"",
klass->strip(iname), "\", ", getfname, ", 0);\n",
0);
if (!(Status & STAT_READONLY)) {
Printv(s,
tab4, "rb_define_singleton_method(", klass->vname, ", \"",
klass->strip(iname), "=\", ", setfname, ", 1);\n",
0);
}
Printv(klass->init,s,0);
break;
default:
// C global variable
if (toplevel) {
// wrapped in Ruby global variable
Printv(s,
tab4, "rb_define_virtual_variable(\"", iname, "\", ",
getfname, ", ", setfname, ");\n",
0);
} else {
// wrapped in Ruby module attribute
Printv(s,
tab4, "rb_define_singleton_method(", modvar, ", \"",
iname, "\", ", getfname, ", 0);\n",
0);
if (!(Status & STAT_READONLY)) {
Printv(s,
tab4, "rb_define_singleton_method(", modvar, ", \"",
iname, "=\", ", setfname, ", 1);\n",
0);
}
}
Printv(f_init,s,0);
Delete(s);
break;
}
Delete(getfname);
Delete(setfname);
DelWrapper(setf);
DelWrapper(getf);
}
// ---------------------------------------------------------------------
// RUBY::validate_const_name(char *name)
//
// Validate constant name.
// ---------------------------------------------------------------------
char *RUBY::validate_const_name(char *name) {
if (!name || name[0] == '\0')
return name;
if (isupper(name[0]))
return name;
if (islower(name[0])) {
name[0] = toupper(name[0]);
Printf(stderr,"%s : Line %d. Wrong constant/class/module name "
"(corrected to `%s')\n", input_file, line_number, name);
return name;
}
Printf(stderr,"%s : Line %d. Wrong constant/class/module name\n",
input_file, line_number);
return name;
}
// ---------------------------------------------------------------------
// RUBY::declare_const(char *name, char *iname, DataType *type, char *value)
//
// Makes a constant.
// name = Name of the constant
// iname = Scripting language name of constant
// type = Datatype of the constant
// value = Constant value (as a string)
// ---------------------------------------------------------------------
void RUBY::declare_const(char *name, char *iname, DataType *type, char *value) {
char *tm;
if (current == CLASS_CONST)
iname = klass->strip(iname);
tm = ruby_typemap_lookup((char*)"const",type,name,value,iname);
if (tm) {
DOHString *str = NewString(tm);
Replace(str,"$value",value, DOH_REPLACE_ANY);
if (current == CLASS_CONST) {
Replace(str,"$module", klass->vname, DOH_REPLACE_ANY);
indent(str);
Printv(klass->init, str, "\n", 0);
} else {
if (toplevel)
Replace(str,"$module", "rb_cObject", DOH_REPLACE_ANY);
else
Replace(str,"$module", modvar, DOH_REPLACE_ANY);
indent(str);
Printf(f_init,"%s\n", str);
}
Delete(str);
} else {
Printf(stderr,"%s : Line %d. Unable to create constant %s = %s\n",
input_file, line_number, DataType_print_type(type), value);
}
}
// ---------------------------------------------------------------------
// RUBY::ruby_typemap_lookup(char *op, DataType *type, char *pname, char *source, char *target, WrapperFunction *f = 0)
//
// Ruby specific typemap_lookup.
// op = string code for type of mapping
// type = the datatype
// pname = an optional parameter name
// source = a string with the source variable
// target = a string containing the target value
// f = a wrapper function object (optional)
// ---------------------------------------------------------------------
char *RUBY::ruby_typemap_lookup(char *op, DataType *type, char *pname, char *source, char *target, Wrapper *f) {
static DOHString *s = 0;
char *tm;
DOHString *target_replace = NewString(target);
target = Char(target_replace);
RClass *cls = RCLASS(classes, type->name);
if (!s) s = NewString("");
Clear(s);
if ((strcmp("out", op) == 0 || strcmp("in", op) == 0)
&& strcmp(type->name, "VALUE") == 0) {
Printf(s,"$target = $source;");
} else if (strcmp("out", op) == 0 && type->type == T_USER &&
type->is_pointer == 1 && cls) {
const char *vname = (current == CONSTRUCTOR ? "self" : Char(cls->vname));
Printv(s, "$target = Wrap_", cls->cname, "(", vname, ", $source);",0);
} else if (strcmp("in", op)==0 && type->type == T_USER &&
type->is_pointer == 1 && cls) {
Printv(s, "Get_", cls->cname, "($source, $target);", 0);
} else {
tm = typemap_lookup(op, typemap_lang, type, pname, source, target, f);
if (tm) {
Delete(target_replace);
return tm;
}
Clear(s);
if (strcmp("in", op) == 0) {
DOHString *v = NewString("");
if (from_VALUE(type, (char*)"$source", v))
Printv(s, "$target = ", v, ";", 0);
Delete(v);
} else if (strcmp("out", op) == 0) {
DOHString *v = NewString("");
if (to_VALUE(type, (char*)"$source", v))
Printv(s, "$target = ", v, ";", 0);
Delete(v);
} else if (strcmp("const", op) == 0) {
DOHString *v = NewString("");
if (to_VALUE(type, (char*)"$value", v, 1)) {
Printv(s, "rb_define_const($module, \"$target\", ", v, ");", 0);
validate_const_name(target);
}
Delete(v);
}
}
if (Len(s) == 0) {
return NULL;
}
if (source && strlen(source) > 0)
Replace(s,"$source",source, DOH_REPLACE_ANY);
if (target && strlen(target) > 0)
Replace(s,"$target",target, DOH_REPLACE_ANY);
Replace(s,"$type", DataType_print_type(type), DOH_REPLACE_ANY);
return Char(s);
}
// ---------------------------------------------------------------------
// RUBY::to_VALUE(DataType *type, char *value, literal)
//
// Makes a VALUE (as a string)
// type = Datatype of the C value
// value = C value (as a string)
// str = resulting code (as a string)
// raw = value is raw string (not quoted) ?
// ---------------------------------------------------------------------
int RUBY::to_VALUE(DataType *type, char *value, DOHString *str, int raw) {
Clear(str);
if (type->is_pointer == 0) {
switch(type->type) {
case T_INT:case T_SINT:
case T_SHORT: case T_SSHORT:
case T_LONG: case T_SLONG:
case T_SCHAR:
Printv(str, "INT2NUM(", value, ")", 0);
break;
case T_UINT:
case T_USHORT:
case T_ULONG:
case T_UCHAR:
Printv(str,"UINT2NUM(", value, ")", 0);
break;
case T_DOUBLE:
case T_FLOAT:
Printv(str, "rb_float_new(", value, ")", 0);
break;
case T_CHAR:
Printv(str, "rb_str_new(&", value, ", 1)", 0);
break;
case T_BOOL:
Printv(str, "(", value, " ? Qtrue : Qfalse)", 0);
break;
default:
break;
}
} else if ((type->type == T_CHAR) && (type->is_pointer == 1)) {
if (raw)
Printv(str, "rb_str_new2(\"", value, "\")", 0);
else
Printv(str, "rb_str_new2(", value, ")", 0);
} else {
Printv(str, "SWIG_NewPointerObj((void *)", value, ", \"", DataType_print_mangle(type), "\")", 0);
}
if (Len(str) == 0)
return 0;
return 1;
}
// ---------------------------------------------------------------------
// RUBY::from_VALUE(DataType *type, char *value)
//
// extract VALUE
// type = Datatype of the C value
// value = Ruby VALUE (as a string)
// str = resulting code (as a string)
// ---------------------------------------------------------------------
int RUBY::from_VALUE(DataType *type, char *value, DOHString *str) {
Clear(str);
if (type->is_pointer == 0) {
switch(type->type) {
case T_INT:case T_SINT:
Printv(str, "NUM2INT(", value, ")", 0);
break;
case T_LONG: case T_SLONG:
Printv(str, "NUM2LONG(", value, ")", 0);
break;
case T_SHORT: case T_SSHORT:
Printv(str, "NUM2SHRT(", value, ")", 0);
break;
case T_UINT:
Printv(str, "NUM2UINT(", value, ")", 0);
break;
case T_ULONG:
Printv(str, "NUM2ULONG(", value, ")", 0);
break;
case T_USHORT:
Printv(str, "NUM2USHRT(", value, ")", 0);
break;
case T_DOUBLE:
case T_FLOAT:
Printv(str, "NUM2DBL(", value, ")", 0);
break;
case T_CHAR: case T_SCHAR: case T_UCHAR:
Printv(str, "NUM2CHR(", value, ")", 0);
break;
case T_BOOL:
Printv(str,"RTEST(", value, ")", 0);
break;
default:
break;
}
} else if ((type->type == T_CHAR) && (type->is_pointer == 1)) {
Printv(str, "STR2CSTR(", value, ")", 0);
} else {
Printv(str, "SWIG_ConvertPtr(", value, ", \"", DataType_print_mangle(type), "\")", 0);
}
if (Len(str) == 0) return 0;
return 1;
}
// ----------------------------------------------------------------------
// void RUBY::cpp_open_class(char *classname, char *classrename, char *ctype, int strip)
//
// Open a new C++ class.
//
// INPUTS:
// name = Real name of the C++ class
// rename = New name of the class (if %name was used)
// ctype = Class type (struct, class, union, etc...)
// strip = Flag indicating whether we should strip the
// class type off
//
// This function is in charge of creating a new class. The SWIG parser has
// already processed the entire class definition prior to calling this
// function (which should simplify things considerably).
//
// ----------------------------------------------------------------------
void RUBY::cpp_open_class(char *cname, char *rename, char *ctype, int strip) {
this->Language::cpp_open_class(cname, rename, ctype, strip);
klass = RCLASS(classes, cname);
if (strip) {
Clear(klass->type);
Append(klass->type, klass->cname);
} else {
Clear(klass->type);
Printv(klass->type, ctype, " ", klass->cname,0);
}
Printv(klass->header, "\nVALUE ", klass->vname, ";\n", 0);
Printv(klass->init, "\n", tab4, 0);
if (toplevel) {
Printv(klass->init, klass->vname, " = rb_define_class(",
"\"", klass->name, "\", $super);\n", 0);
} else {
Printv(klass->init, klass->vname, " = rb_define_class_under(", modvar,
", \"", klass->name, "\", $super);\n", 0);
}
Replace(klass->includes,"$class", klass->vname, DOH_REPLACE_ANY);
Printv(klass->init, klass->includes,0);
Printv(klass->init, "$constructor",0);
Printv(klass->header,
"$markproto",
"$freeproto",
"#define Wrap_", klass->cname, "(klass, ptr) (\\\n",
tab4, "(ptr) ? Data_Wrap_Struct(klass",
", $markfunc, $freefunc, ptr) : Qnil )\n",
"#define Get_", klass->cname, "(val, ptr) {\\\n",
tab4, "if (NIL_P(val)) ptr = NULL;\\\n",
tab4, "else {\\\n",
tab8, "if (!rb_obj_is_kind_of(val, ", klass->vname, "))\\\n",
tab8, tab4, "rb_raise(rb_eTypeError, \"wrong argument type",
" (expected ", klass->name, ")\");\\\n",
tab8, "Data_Get_Struct(val, ", klass->type, ", ptr);\\\n",
tab8, "if (!ptr) rb_raise(rb_eRuntimeError, \"",
"This ", klass->name, " already released\");\\\n",
tab4, "}\\\n",
"}\n",
0);
}
// ---------------------------------------------------------------------
// void RUBY::cpp_close_class()
//
// Close the current class
// ---------------------------------------------------------------------
void RUBY::cpp_close_class() {
this->Language::cpp_close_class();
Replace(klass->header,"$markfunc", "0", DOH_REPLACE_ANY);
Replace(klass->header,"$markproto", "", DOH_REPLACE_ANY);
if (!klass->destructor_defined) {
Replace(klass->header,"$freefunc", "0", DOH_REPLACE_ANY);
Replace(klass->header,"$freeproto", "", DOH_REPLACE_ANY);
}
Printv(f_header, klass->header,0);
Replace(klass->aliases,"$class", klass->vname, DOH_REPLACE_ANY);
Printv(klass->init, klass->aliases,0);
DOHString *s = NewString("");
Printv(s, tab4, "rb_undef_method(CLASS_OF(", klass->vname,
"), \"new\");\n", 0);
Replace(klass->init,"$constructor", s, DOH_REPLACE_ANY);
Replace(klass->init,"$super", "rb_cObject", DOH_REPLACE_ANY);
Printv(f_init,klass->init,0);
klass = 0;
}
// ----------------------------------------------------------------------
// void RUBY::cpp_inherit(char **baseclass, int mode)
//
// Inherit attributes from given baseclass.
//
// INPUT:
// baseclass = NULL terminate list of baseclasses
//
// ---------------------------------------------------------------------
void RUBY::cpp_inherit(char **baseclass, int mode) {
if (!baseclass)
return;
for (int i = 0; baseclass[i]; i++) {
RClass *super = RCLASS(classes, baseclass[i]);
if (super) {
Replace(klass->init,"$super", super->vname, DOH_REPLACE_ANY);
break; // ignore multiple inheritance
}
}
}
// ----------------------------------------------------------------------
// void RUBY::cpp_member_func(char *name, char *iname, DataType *t, ParmList *l)
//
// Method for adding C++ member function
//
// INPUTS:
// name - name of the member function
// iname - renaming (if given)
// t - Return datatype
// l - Parameter list
//
// By default, we're going to create a function of the form :
//
// Foo_bar(this,args)
//
// Where Foo is the classname, bar is the member name and the this pointer
// is explicitly attached to the beginning.
//
// The renaming only applies to the member function part, not the full
// classname.
//
// ---------------------------------------------------------------------
void RUBY::cpp_member_func(char *name, char *iname, DataType *t, ParmList *l) {
current = MEMBER_FUNC;
this->Language::cpp_member_func(name, iname, t, l);
current = NO_CPP;
}
// ---------------------------------------------------------------------
// void RUBY::cpp_constructor(char *name, char *iname, ParmList *l)
//
// Method for adding C++ member constructor
//
// INPUTS:
// name - Name of the constructor (usually same as classname)
// iname - Renamed version
// l - parameters
// --------------------------------------------------------------------
void RUBY::cpp_constructor(char *name, char *iname, ParmList *l) {
current = CONSTRUCTOR;
this->Language::cpp_constructor(name, iname, l);
current = NO_CPP;
}
// ---------------------------------------------------------------------
// void RUBY::cpp_destructor(char *name, char *iname)
//
// Method for adding C++ member destructor
//
// INPUT:
// name - Name of the destructor (classname)
// iname - Renamed destructor
//
// --------------------------------------------------------------------
void RUBY::cpp_destructor(char *name, char *newname) {
current = DESTRUCTOR;
this->Language::cpp_destructor(name, newname);
DOHString *freefunc = NewString("");
DOHString *freeproto = NewString("");
DOHString *freebody = NewString("");
Printv(freefunc, "free_", klass->cname, 0);
Printv(freeproto, "static void ", freefunc, "(", klass->type, " *);\n", 0);
Printv(freebody, "static void\n",
freefunc, "(", klass->type, " *ptr) {\n",
tab4, Swig_name_destroy(name), "(ptr);\n",
"}\n",
0);
if (CPlusPlus) {
Insert(freefunc,0,"VOIDFUNC(");
Append(freefunc,")");
}
Replace(klass->header,"$freefunc", freefunc, DOH_REPLACE_ANY);
Replace(klass->header,"$freeproto", freeproto, DOH_REPLACE_ANY);
Printv(f_wrappers, freebody, 0);
klass->destructor_defined = 1;
current = NO_CPP;
Delete(freefunc);
Delete(freeproto);
Delete(freebody);
}
// ---------------------------------------------------------------------
// void RUBY::cpp_variable(char *name, char *iname, DataType *t)
//
// Wrap a C++ data member
//
// INPUTS :
// name = Name of the C++ member
// iname = Name as used in the interpreter
// t = Datatype
//
// This creates a pair of functions to set/get the variable of a member.
// --------------------------------------------------------------------
void RUBY::cpp_variable(char *name, char *iname, DataType *t) {
current = MEMBER_VAR;
this->Language::cpp_variable(name, iname, t);
current = NO_CPP;
}
// -----------------------------------------------------------------------
// void RUBY::cpp_static_func(char *name, char *iname, DataType *t, ParmList *l)
//
// Wrap a static C++ function
//
// INPUTS:
// name = Real name of the function
// iname = New name in interpreter
// t = Return datatype
// l = Parameters
// ----------------------------------------------------------------------
void RUBY::cpp_static_func(char *name, char *iname, DataType *t, ParmList *l) {
current = STATIC_FUNC;
this->Language::cpp_static_func(name, iname, t, l);
current = NO_CPP;
}
// ----------------------------------------------------------------------
// void RUBY::cpp_declare_const(char *name, char *iname, DataType *t, char *value)
//
// Create a C++ constant
//
// INPUTS :
// name = Real name of the constant
// iname = new name
// t = Datatype
// value = value as a string
//
// ---------------------------------------------------------------------
void RUBY::cpp_declare_const(char *name, char *iname, DataType *type, char *value) {
current = CLASS_CONST;
this->Language::cpp_declare_const(name, iname, type, value);
current = NO_CPP;
}
// ---------------------------------------------------------------------
// void RUBY::cpp_static_var(char *name, char *iname, DataType *t)
//
// Wrap a static C++ variable
//
// INPUT :
// name = name of the variable
// iname = interpreter name
// t = Datatype
//
// ---------------------------------------------------------------------
void RUBY::cpp_static_var(char *name, char *iname, DataType *t) {
current = STATIC_VAR;
this->Language::cpp_static_var(name, iname, t);
current = NO_CPP;
}
// -----------------------------------------------------------------------
// RUBY::cpp_class_decl(char *name, char *rename, char *type)
//
// A forward class declaration
// -----------------------------------------------------------------------
void RUBY::cpp_class_decl(char *cname, char *rename, char *type) {
DOHString *valid_name = NewString((rename ? rename : cname));
validate_const_name(Char(valid_name));
klass->set_name(cname, rename, Char(valid_name));
SET_RCLASS(classes, cname, klass);
if (type && strlen(type) > 0) {
char temp[256];
sprintf(temp,"%s %s", type, cname);
SET_RCLASS(classes, temp, klass);
}
char s[256];
sprintf(s,"extern VALUE %s;\n", Char(klass->vname));
Printf(f_header, s);
klass = new RClass();
Delete(valid_name);
}
// --------------------------------------------------------------------
// void RUBY::pragma(char *target, char *var, char *value)
//
// A pragma declaration
// --------------------------------------------------------------------
void RUBY::pragma(char *lang, char *cmd, char *value) {
if (strcmp(lang, "ruby") != 0)
return;
if (strcmp(cmd, "free") == 0) {
char name[64];
if (sscanf(value, " %s ", name) != 1) {
Printf(stderr, "%s : Line %d. Invalid free pragma.\n",
input_file, line_number);
return;
}
Setattr(klass->freemethods, name, name);
} else if (strcmp(cmd, "include") == 0) {
char name[64];
if (sscanf(value, " %s ", name) != 1) {
Printf(stderr, "%s : Line %d. Invalid include pragma.\n",
input_file, line_number);
return;
}
Printv(klass->includes,tab4, "rb_include_module($class, ",
"rb_eval_string(\"", name, "\"));\n", 0);
} else if (strcmp(cmd, "alias") == 0) {
char alias[64], name[64];
if (sscanf(value, " %s %s ", alias, name) != 2) {
Printf(stderr, "%s : Line %d. Invalid alias pragma.\n",
input_file, line_number);
return;
}
Printv(klass->aliases, tab4, "rb_define_alias($class, ",
"\"", alias, "\", \"", name, "\");\n", 0);
} else if (strcmp(cmd, "pred") == 0) {
char *tok;
tok = strtok(value, " \t");
while (tok) {
Setattr(klass->predmethods, tok, tok);
tok = strtok(0, " \t");
}
} else if (strcmp(cmd, "debug") == 0) {
Printf(f_header, "/* %s */\n", value);
Printf(f_wrappers, "/* %s */\n", value);
Printf(f_init, "/* %s */\n", value);
Printf(stderr, "%s\n", value);
} else {
Printf(stderr, "%s : Line %d. Unrecognized pragma.\n",
input_file, line_number);
}
}
// -----------------------------------------------------------------------------
// RUBY::cpp_pragma(Pragma *plist)
//
// Handle C++ pragmas
// -----------------------------------------------------------------------------
void RUBY::cpp_pragma(Pragma *plist) {
while (plist) {
pragma(Char(plist->lang), Char(plist->name), Char(plist->value));
plist = plist->next;
}
}
// ---------------------------------------------------------------------
// RUBY::import(char *filename)
//
// Imports a SWIG module as a separate file.
//----------------------------------------------------------------------
void RUBY::import(char *filename) {
if (import_file) free(import_file);
import_file = Swig_copy_string(filename);
}
/*
* Local Variables:
* c-basic-offset: 2
* End:
*/