1
// ================================================================ //
3
// File : arb_proto_2_xsub.cxx //
4
// Purpose : generate ARB.xs for perl interface //
6
// ReCoded by Ralf Westram (coder@reallysoft.de) in December 2009 //
7
// Institute of Microbiology (Technical University Munich) //
8
// http://www.arb-home.de/ //
10
// ================================================================ //
13
#include <arb_strbuf.h>
14
#include <BufferedFileReader.h>
26
#if defined(DEVEL_RALF)
33
// --------------------------------------------------------------------------------
35
#define CHAR_PTR "char *"
36
#define CONST_CHAR_PTR "const char *"
38
// --------------------------------------------------------------------------------
42
virtual void print() const = 0;
45
class ProgramError : public Error {
48
ProgramError(string message) : error(message) {}
49
ProgramError(const char *message) : error(message) {}
50
virtual ~ProgramError() OVERRIDE {}
52
void print() const OVERRIDE {
53
fprintf(stderr, "arb_proto_2_xsub: Error: %s\n", error.c_str());
57
class InputFileError : public Error {
60
InputFileError(LineReader& fileBuffer, string message) : located_error(fileBuffer.lineError(message)) {}
61
InputFileError(LineReader& fileBuffer, const char *message) : located_error(fileBuffer.lineError(message)) {}
62
virtual ~InputFileError() OVERRIDE {}
64
void print() const OVERRIDE {
65
fputs(located_error.c_str(), stderr);
70
// --------------------------------------------------------------------------------
72
class CommentSkippingFileBuffer : public BufferedFileReader {
77
void throw_error(const char *message) __ATTR__NORETURN { throw InputFileError(*this, message); }
79
string read_till_close_comment(string curr_line, size_t comment_startLineNumber) {
80
bool seen_end = false;
82
size_t close = curr_line.find(close_comment);
83
if (close != string::npos) {
84
curr_line = curr_line.substr(close+close_comment.length());
88
if (!BufferedFileReader::getLine(curr_line)) {
89
setLineNumber(comment_startLineNumber);
90
throw_error("end of file reached while skipping comment");
98
CommentSkippingFileBuffer(const string& filename_,
100
const char *openComment,
101
const char *closeComment,
102
const char *eolComment)
103
: BufferedFileReader(filename_, in)
104
, open_comment(openComment)
105
, close_comment(closeComment)
106
, eol_comment(eolComment)
109
bool getLine(string& line) OVERRIDE {
110
if (BufferedFileReader::getLine(line)) {
111
size_t open = line.find(open_comment);
112
size_t eol = line.find(eol_comment);
114
if (open != eol) { // comment found
116
if (eol != string::npos) {
117
throw_error(GBS_global_string("'%s' inside '%s %s'", eol_comment.c_str(), open_comment.c_str(), close_comment.c_str()));
119
line = line.substr(0, open) + read_till_close_comment(line.substr(open+2), getLineNumber());
122
arb_assert(eol<open);
123
if (open != string::npos) {
124
throw_error(GBS_global_string("'%s' behind '%s'", open_comment.c_str(), eol_comment.c_str()));
126
line = line.substr(0, eol);
135
// --------------------------------------------------------------------------------
137
inline bool is_empty_code(const char *code) { return !code[0]; }
138
inline bool contains_preprozessorCode(const char *code) { return strchr(code, '#') != NULL; }
139
inline bool contains_braces(const char *code) { return strpbrk(code, "{}") != NULL; }
140
inline bool is_typedef(const char *code) { return ARB_strBeginsWith(code, "typedef"); }
141
inline bool is_forward_decl(const char *code) { return ARB_strBeginsWith(code, "class") || ARB_strBeginsWith(code, "struct"); }
143
inline bool is_prototype(const char *code) {
145
!is_empty_code(code) &&
146
!contains_preprozessorCode(code) &&
147
!contains_braces(code) &&
149
!is_forward_decl(code);
152
inline void trace_over_braces(const char *code, int& brace_counter) {
154
const char *brace = strpbrk(code, "{}");
161
arb_assert(*brace == '}');
168
// --------------------------------------------------------------------------------
170
inline char *get_token_and_incr_lineno(const char*& code, const char *separator, size_t *lineno) {
173
const char *sep_pos = strpbrk(code, separator);
176
if (!code[0]) { // end of code
181
token = strdup(code);
186
token = GB_strpartdup(code, sep_pos-1);
188
const char *behind_sep = sep_pos + strspn(sep_pos, separator); // next non 'separator' char
190
int no_of_linefeeds = 0;
191
while (code<behind_sep) if (*++code == '\n') ++no_of_linefeeds;
193
*lineno += no_of_linefeeds;
203
inline char *get_token(const char*& code, const char *separator) {
204
return get_token_and_incr_lineno(code, separator, NULL);
207
inline bool is_ID_char(char c) { return isalnum(c) || c == '_'; }
209
inline const char *next_closing_paren(const char *code) {
210
const char *open_paren = strchr(code, '(');
211
const char *close_paren = strchr(code, ')');
213
if (!open_paren || (close_paren && close_paren<open_paren)) return close_paren;
215
close_paren = next_closing_paren(open_paren+1);
216
return next_closing_paren(close_paren+1);
219
inline const char *next_comma_outside_parens(const char *code) {
220
const char *comma = strchr(code, ',');
222
const char *open_paren = strchr(code, '(');
223
if (open_paren && open_paren<comma) {
224
const char *close_paren = next_closing_paren(open_paren+1);
225
if (!close_paren) throw "Unbalanced parenthesis";
226
comma = next_comma_outside_parens(close_paren+1);
232
inline bool find_open_close_paren(const char *code, size_t& opening_paren_pos, size_t& closing_paren_pos) {
233
const char *open_paren = strchr(code, '(');
235
const char *close_paren = next_closing_paren(open_paren+1);
237
opening_paren_pos = open_paren-code;
238
closing_paren_pos = close_paren-code;
245
inline string concat_type_and_name(const string& type, const string& name) {
246
if (type.at(type.length()-1) == '*') return type+name;
247
return type+' '+name;
254
// representation of types mapped in 'typemap' file
255
set<string> defined_types;
260
void load(LineReader& typemap);
261
bool has_definition_for(const string& type_decl) const {
262
return defined_types.find(type_decl) != defined_types.end();
270
INVALID_TYPE, // invalid
272
VOID, // no parameter
273
SIMPLE_TYPE, // simple types like int, float, double, ...
274
CONST_CHAR, // 'const char *'
275
HEAP_COPY, // type is 'char*' and interpreted as heap-copy
276
CONVERSION_FUNCTION, // convert type using GBP_-conversion functions
277
TYPEMAPPED, // type is defined in file 'typemap'
279
CANT_HANDLE, // type cannot be used in perl interface
280
FORBIDDEN, // usage forbidden via 'NOT4PERL'
284
#define TypeClass2CSTR(type) case type: return #type
285
const char *get_TypeClass_name(TypeClass type_class) {
286
switch (type_class) {
287
TypeClass2CSTR(INVALID_TYPE);
288
TypeClass2CSTR(VOID);
289
TypeClass2CSTR(SIMPLE_TYPE);
290
TypeClass2CSTR(CONST_CHAR);
291
TypeClass2CSTR(HEAP_COPY);
292
TypeClass2CSTR(CONVERSION_FUNCTION);
293
TypeClass2CSTR(TYPEMAPPED);
294
TypeClass2CSTR(CANT_HANDLE);
295
TypeClass2CSTR(FORBIDDEN);
299
#undef TypeClass2CSTR
302
inline string type2id(const string& type) {
303
char *s = GBS_string_eval(type.c_str(),
304
"const =:" // remove const (for less ugly names)
305
" =:" // remove spaces
306
"\\*=Ptr" // rename '*'
313
inline string conversion_function_name(const string& fromType, const string& toType) {
314
string from = type2id(fromType);
315
string to = type2id(toType);
316
return string("GBP_")+from+"_2_"+to;
318
inline string constCastTo(const string& expr, const string& targetType) {
319
return string("const_cast<")+targetType+">("+expr+")";
322
class Type { // return- or parameter-type
325
TypeClass type_class;
327
string unify_type_decl(const char *code) {
329
enum { SPACE, STAR, ID, UNKNOWN } last = SPACE, curr;
330
for (int i = 0; code[i]; ++i) {
334
case ' ': curr = SPACE; break;
335
case '*': curr = STAR; break;
336
default: curr = is_ID_char(c) ? ID : UNKNOWN; break;
339
if (last != SPACE && curr != last) type_decl += ' ';
340
if (curr != SPACE) type_decl += c;
346
? type_decl.substr(0, type_decl.length()-1)
350
void throw_if_enum() const {
351
size_t enum_pos = c_type.find("enum ");
352
if (enum_pos != string::npos) {
353
const char *enum_type = c_type.c_str()+enum_pos;
354
const char *enum_name = enum_type+5;
355
throw GBS_global_string("do not use '%s', simply use '%s'", enum_type, enum_name);
359
string convertExpression(const string& expr, const string& fromType, const string& toType) const {
360
arb_assert(possible_in_xsub());
362
if (get_TypeClass() == CONVERSION_FUNCTION) {
363
string conversion_function = conversion_function_name(fromType, toType);
364
return conversion_function+"("+expr+")"; // result is toType
369
bool cant_handle(const string& type_decl) {
371
strpbrk(type_decl.c_str(), "().*") != NULL || // function-parameters, pointer-types not handled in ctor
372
type_decl.find("GB_CB") != string::npos || // some typedef'ed function-parameters
373
type_decl.find("CharPtrArray") != string::npos ||
374
type_decl.find("StrArray") != string::npos ||
375
type_decl.find("GB_Link_Follower") != string::npos;
378
bool is_forbidden(const string& type_decl) {
380
type_decl.find("NOT4PERL") != string::npos || // 'NOT4PERL' declares prototype as "FORBIDDEN"
381
type_decl.find("GBQUARK") != string::npos || // internal information, hide from perl
382
type_decl.find("GB_COMPRESSION_MASK") != string::npos || // internal information, hide from perl
383
type_decl.find("GB_CBUFFER") != string::npos || // internal ARBDB buffers
384
type_decl.find("GB_BUFFER") != string::npos; // memory managed by ARBDB
389
static TypeMap globalTypemap;
391
Type() : type_class(INVALID_TYPE) {}
392
Type(const char *code) {
393
c_type = unify_type_decl(code);
395
if (c_type == "void") { type_class = VOID; }
396
else if (c_type == CONST_CHAR_PTR ||
397
c_type == "GB_ERROR" ||
400
type_class = CONST_CHAR;
401
perl_type = CHAR_PTR;
403
else if (c_type == CHAR_PTR) {
404
type_class = HEAP_COPY;
407
// [Code-TAG: enum_type_replacement]
408
// for each enum type converted here, you need to support a
409
// conversion function in ../ARBDB/adperl.cxx@enum_conversion_functions
410
else if (c_type == "GB_CASE" ||
411
c_type == "GB_CB_TYPE" ||
412
c_type == "GB_TYPES" ||
413
c_type == "GB_UNDO_TYPE" ||
414
c_type == "GB_SEARCH_TYPE" ||
415
c_type == "GB_alignment_type")
417
type_class = CONVERSION_FUNCTION;
418
perl_type = CHAR_PTR;
420
else if (globalTypemap.has_definition_for(c_type)) {
421
type_class = TYPEMAPPED;
424
else if (cant_handle(c_type)) { type_class = CANT_HANDLE; }
425
else if (is_forbidden(c_type)) { type_class = FORBIDDEN; } // Caution: this line catches all '*' types not handled above
427
type_class = SIMPLE_TYPE;
432
const string& c_decl() const { return c_type; }
433
const string& perl_decl() const { return perl_type; }
435
TypeClass get_TypeClass() const { return type_class; }
436
bool isVoid() const { return get_TypeClass() == VOID; }
438
bool possible_in_xsub() const { return type_class != CANT_HANDLE && type_class != FORBIDDEN; }
440
string convert_argument_for_C(const string& perl_arg) const {
441
if (perl_decl() == CHAR_PTR) {
442
if (c_decl() == CHAR_PTR) throw "argument of type 'char*' is forbidden";
443
string const_perl_arg = constCastTo(perl_arg, CONST_CHAR_PTR); // ensure C uses 'const char *'
444
return convertExpression(const_perl_arg, CONST_CHAR_PTR, c_decl());
446
return convertExpression(perl_arg, perl_decl(), c_decl());
448
string convert_result_for_PERL(const string& c_expr) const {
449
arb_assert(type_class != HEAP_COPY);
450
if (perl_decl() == CHAR_PTR) {
451
string const_c_expr = convertExpression(c_expr, c_decl(), CONST_CHAR_PTR);
452
return constCastTo(const_c_expr, CHAR_PTR);
454
return convertExpression(c_expr, c_decl(), perl_decl());
458
void dump_if_impossible_in_xsub(FILE *out) const {
459
if (!possible_in_xsub()) {
460
fprintf(out, "TRACE: - impossible type '%s' (TypeClass='%s')\n",
461
c_type.c_str(), get_TypeClass_name(type_class));
468
TypeMap Type::globalTypemap;
470
// ------------------
477
static long nonameCount;
481
Parameter(const char *code) {
482
const char *last = strchr(code, 0)-1;
483
while (last[0] == ' ') --last;
485
const char *name_start = last;
486
while (name_start >= code && is_ID_char(name_start[0])) --name_start;
489
if (name_start>code) {
490
string type_def(code, name_start-code);
491
name = string(name_start, last-name_start+1);
492
type = Type(type_def.c_str());
494
if (type.possible_in_xsub() && !type.isVoid() && name.empty()) {
495
name = GBS_global_string("noName%li", ++nonameCount);
498
else if (strcmp(name_start, "void") == 0) {
499
string no_type(name_start, last-name_start+1);
501
type = Type(no_type.c_str());
504
throw string("can't parse '")+code+"' (expected 'type name')";
508
const string& get_name() const { return name; }
509
const Type& get_type() const { return type; }
511
TypeClass get_TypeClass() const { return get_type().get_TypeClass(); }
512
bool isVoid() const { return get_TypeClass() == VOID; }
514
string perl_typed_param() const { return concat_type_and_name(type.perl_decl(), name); }
515
string c_typed_param () const { return concat_type_and_name(type.c_decl (), name); }
518
long Parameter::nonameCount = 0;
520
// ------------------
523
typedef vector<Parameter> Arguments;
524
typedef Arguments::const_iterator ArgumentIter;
527
Parameter function; // return-type + function_name
530
void parse_arguments(const char *arg_list) {
531
const char *comma = next_comma_outside_parens(arg_list);
534
char *first_param = GB_strpartdup(arg_list, comma-1);
535
arguments.push_back(Parameter(first_param));
538
parse_arguments(comma+1);
540
else { // only one parameter
541
arguments.push_back(Parameter(arg_list));
546
Prototype(const char *code) {
547
size_t open_paren, close_paren;
548
if (!find_open_close_paren(code, open_paren, close_paren)) {
549
throw "expected parenthesis";
552
string return_type_and_name(code, open_paren);
553
function = Parameter(return_type_and_name.c_str());
555
string arg_list(code+open_paren+1, close_paren-open_paren-1);
556
parse_arguments(arg_list.c_str());
559
const Type& get_return_type() const { return function.get_type(); }
560
const string& get_function_name() const { return function.get_name(); }
562
ArgumentIter args_begin() const { return arguments.begin(); }
563
ArgumentIter args_end() const { return arguments.end(); }
565
string argument_names_list() const {
566
string argument_list;
568
ArgumentIter arg_end = arguments.end();
570
for (ArgumentIter param = arguments.begin(); param != arg_end; ++param) {
571
if (!param->isVoid()) {
572
if (first) first = false;
573
else argument_list += ", ";
575
argument_list += param->get_name();
578
return argument_list;
581
string call_arguments() const {
582
string argument_list;
584
ArgumentIter arg_end = arguments.end();
586
for (ArgumentIter arg = arguments.begin(); arg != arg_end; ++arg) {
587
if (!arg->isVoid()) {
588
if (first) first = false;
589
else argument_list += ", ";
591
argument_list += arg->get_type().convert_argument_for_C(arg->get_name());
594
return argument_list;
597
bool possible_as_xsub() const {
598
if (get_return_type().possible_in_xsub()) {
599
ArgumentIter arg_end = arguments.end();
600
for (ArgumentIter arg = arguments.begin(); arg != arg_end; ++arg) {
601
if (!arg->get_type().possible_in_xsub()) {
611
void dump_types_impossible_in_xsub(FILE *out) const {
612
get_return_type().dump_if_impossible_in_xsub(out);
613
ArgumentIter arg_end = arguments.end();
614
for (ArgumentIter arg = arguments.begin(); arg != arg_end; ++arg) {
615
arg->get_type().dump_if_impossible_in_xsub(out);
621
inline void trim(string& text) {
622
const char *whiteSpace = " \t";
623
size_t leading = text.find_first_not_of(whiteSpace);
624
size_t trailing = text.find_last_not_of(whiteSpace, leading);
626
if (trailing != string::npos) {
627
text = text.substr(leading, text.length()-leading-trailing);
631
void TypeMap::load(LineReader& typemap_reader) {
633
while (typemap_reader.getLine(line)) {
634
if (line == "TYPEMAP") {
635
while (typemap_reader.getLine(line)) {
638
Parameter typemapping(line.c_str());
639
const string& c_type = typemapping.get_type().c_decl();
640
defined_types.insert(c_type);
647
throw InputFileError(typemap_reader, "Expected to see 'TYPEMAP'");
653
class Package : virtual Noncopyable {
654
string prefix; // e.g. 'P2A' or 'P2AT'
655
string name; // e.g. 'ARB' or 'BIO'
656
GBS_strstruct *generated_code;
657
GB_HASH *functions_to_skip;
660
Package(const char *name_, const char *prefix_)
664
generated_code = GBS_stropen(100000);
665
functions_to_skip = GBS_create_hash(1000, GB_MIND_CASE);
668
GBS_free_hash(functions_to_skip);
669
GBS_strforget(generated_code);
672
bool matches_package_prefix(const string& text) const { return text.find(prefix) == 0 && text.at(prefix.length()) == '_'; }
674
void mark_function_defined(const string& function) { GBS_write_hash(functions_to_skip, function.c_str(), 1); }
675
bool not_defined(const string& function) const { return GBS_read_hash(functions_to_skip, function.c_str()) == 0; }
677
const string& get_prefix() const { return prefix; }
679
void append_code(const string& code) { GBS_strncat(generated_code, code.c_str(), code.length()); }
680
void append_code(const char *code) { append_code(string(code)); }
681
void append_code(char code) { GBS_chrcat(generated_code, code); }
683
void append_linefeed(size_t count = 1) { while (count--) append_code("\n"); }
685
void print_xsubs(FILE *file) {
686
fputs("# --------------------------------------------------------------------------------\n", file);
687
fprintf(file, "MODULE = ARB PACKAGE = %s PREFIX = %s_\n\n", name.c_str(), prefix.c_str());
688
fputs(GBS_mempntr(generated_code), file);
692
// ----------------------
695
class xsubGenerator {
699
void generate_xsub(const Prototype& prototype);
706
void mark_handcoded_functions(BufferedFileReader& handcoded) {
708
while (handcoded.getLine(line)) {
709
Package *package = NULL;
711
if (arb.matches_package_prefix(line)) package = &arb;
712
else if (bio.matches_package_prefix(line)) package = &bio;
715
size_t open_paren = line.find('(');
716
if (open_paren != string::npos) {
717
package->mark_function_defined(line.substr(0, open_paren));
725
void generate_all_xsubs(LineReader& prototype_reader);
727
void print_xsubs(FILE *out) {
728
arb.print_xsubs(out);
729
bio.print_xsubs(out);
733
inline string prefix_before(const string& name, char separator) {
734
size_t sep_offset = name.find_first_of(separator);
735
if (sep_offset != string::npos) {
736
return name.substr(0, sep_offset);
741
inline void GBS_spaces(GBS_strstruct *out, int space_count) {
742
const char *spaces = " ";
743
arb_assert(space_count <= 10);
744
GBS_strncat(out, spaces+(10-space_count), space_count);
747
void xsubGenerator::generate_xsub(const Prototype& prototype) {
748
const string& c_function_name = prototype.get_function_name();
749
string function_prefix = prefix_before(c_function_name, '_');
750
Package *package = NULL;
752
if (function_prefix == "GB" || function_prefix == "GBC") {
755
else if (function_prefix == "GBT" || function_prefix == "GEN") {
760
string perl_function_name = package->get_prefix() + c_function_name.substr(function_prefix.length());
762
if (package->not_defined(perl_function_name)) {
763
package->mark_function_defined(perl_function_name); // do NOT xsub functions twice
765
// generate xsub function header
766
const Type& return_type = prototype.get_return_type();
768
string argument_names_list = prototype.argument_names_list();
769
string function_header = return_type.isVoid() ? "void" : return_type.perl_decl();
771
function_header += '\n';
772
function_header += perl_function_name+'('+argument_names_list+")\n";
774
ArgumentIter arg_end = prototype.args_end();
775
for (ArgumentIter arg = prototype.args_begin(); arg != arg_end; ++arg) {
776
if (!arg->isVoid()) {
777
string type_decl = string(" ") + arg->perl_typed_param() + '\n';
778
function_header += type_decl;
782
package->append_code(function_header);
783
package->append_linefeed();
786
// generate xsub function body
787
string call_c_function = c_function_name+'('+prototype.call_arguments()+")";
788
if (return_type.isVoid()) {
789
package->append_code(" PPCODE:\n ");
790
package->append_code(call_c_function);
791
package->append_code(';');
794
string assign_RETVAL = " ";
796
switch (return_type.get_TypeClass()) {
798
case CONVERSION_FUNCTION:
801
assign_RETVAL = string(" RETVAL = ") + return_type.convert_result_for_PERL(call_c_function);
805
// temporarily store heapcopy in static pointer
806
// defined at ../PERL2ARB/ARB_ext.c@static_pntr
808
string(" freeset(static_pntr, ") + call_c_function+");\n"+
809
" RETVAL = static_pntr";
821
assign_RETVAL + ";\n" +
826
package->append_code(body);
828
package->append_linefeed(3);
832
fprintf(stderr, "TRACE: '%s' skipped\n", c_function_name.c_str());
838
fprintf(stderr, "TRACE: Skipped function: '%s' (prefix='%s')\n", c_function_name.c_str(), function_prefix.c_str());
843
static void print_prototype_parse_error(LineReader& prototype_reader, const char *err, const char *prototype) {
844
InputFileError(prototype_reader, GBS_global_string("%s (can't xsub '%s')", err, prototype)).print();
847
void xsubGenerator::generate_all_xsubs(LineReader& prototype_reader) {
848
bool error_occurred = false;
850
int open_brace_counter = 0;
852
while (prototype_reader.getLine(line)) {
853
const char *lineStart = line.c_str();
854
size_t leading_whitespace = strspn(lineStart, " \t");
855
const char *prototype = lineStart+leading_whitespace;
857
if (!open_brace_counter && is_prototype(prototype)) {
859
Prototype proto(prototype);
860
if (proto.possible_as_xsub()) {
861
generate_xsub(proto);
865
fprintf(stderr, "TRACE: prototype '%s' not possible as xsub\n", prototype);
866
proto.dump_types_impossible_in_xsub(stderr);
871
print_prototype_parse_error(prototype_reader, err.c_str(), prototype);
872
error_occurred = true;
874
catch(const char *err) {
875
print_prototype_parse_error(prototype_reader, err, prototype);
876
error_occurred = true;
878
catch(...) { arb_assert(0); }
882
fprintf(stderr, "TRACE: not a prototype: '%s'\n", prototype);
884
trace_over_braces(prototype, open_brace_counter);
888
if (error_occurred) throw ProgramError("could not generate xsubs for all prototypes");
891
static void print_xs_default(BufferedFileReader& xs_default, const char *proto_filename, FILE *out) {
893
"/* This file has been generated from\n"
897
xs_default.getFilename().c_str(),
900
xs_default.copyTo(out);
904
static BufferedFileReader *createFileBuffer(const char *filename) {
905
FILE *in = fopen(filename, "rt");
907
GB_export_IO_error("reading", filename);
908
throw ProgramError(GB_await_error());
910
return new BufferedFileReader(filename, in);
912
static BufferedFileReader *createCommentSkippingFileBuffer(const char *filename) {
913
FILE *in = fopen(filename, "rt");
915
GB_export_IO_error("reading", filename);
916
throw ProgramError(GB_await_error());
918
return new CommentSkippingFileBuffer(filename, in, "/*", "*/", "//");
923
static void loadTypemap(const char *typemap_filename) {
924
SmartPtr<BufferedFileReader> typemap = createFileBuffer(typemap_filename);
925
Type::globalTypemap.load(*typemap);
928
int ARB_main(int argc, char **argv)
930
bool error_occurred = false;
933
fputs("arb_proto_2_xsub converts GB_prototypes for the ARB perl interface\n"
934
"Usage: arb_proto_2_xsub <prototypes.h> <xs-header> <typemap>\n"
935
" <prototypes.h> contains prototypes of ARBDB library\n"
936
" <xs-header> may contain prototypes, which will not be\n"
937
" overwritten by generated default prototypes\n"
938
" <typemap> contains type-conversion-definitions, which can\n"
939
" be handled by xsubpp\n"
942
throw ProgramError("Wrong number of command line arguments");
945
const char *proto_filename = argv[1];
946
const char *xs_default_name = argv[2];
947
const char *typemap_filename = argv[3];
949
loadTypemap(typemap_filename);
952
SmartPtr<BufferedFileReader> xs_default = createFileBuffer(xs_default_name);
954
xsubGenerator generator;
955
generator.mark_handcoded_functions(*xs_default);
957
SmartPtr<BufferedFileReader> prototypes = createCommentSkippingFileBuffer(proto_filename);
958
generator.generate_all_xsubs(*prototypes);
963
print_xs_default(*xs_default, proto_filename, out);
964
generator.print_xsubs(out);
969
error_occurred = true;
972
ProgramError("Unexpected exception").print();
973
error_occurred = true;
976
if (error_occurred) {
977
ProgramError("failed").print();
984
// --------------------------------------------------------------------------------
988
#include <test_unit.h>
990
#include <ut_valgrinded.h>
992
// #define TEST_AUTO_UPDATE // uncomment this to update expected results
994
inline GB_ERROR valgrinded_system(const char *cmdline) {
995
char *cmddup = strdup(cmdline);
996
make_valgrinded_call(cmddup);
998
GB_ERROR error = GBK_system(cmddup);
1003
void TEST_arb_proto_2_xsub() {
1004
TEST_EXPECT_ZERO(chdir("xsub"));
1006
const char *outname = "ap2x.out";
1007
const char *expected = "ap2x.out.expected";
1009
char *cmd = GBS_global_string_copy("arb_proto_2_xsub ptype.header default.xs typemap > %s", outname);
1010
TEST_EXPECT_NO_ERROR(valgrinded_system(cmd));
1012
#if defined(TEST_AUTO_UPDATE)
1013
system(GBS_global_string("cp %s %s", outname, expected));
1015
TEST_EXPECT_TEXTFILE_DIFFLINES(expected, outname, 0);
1017
TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outname));
1022
#endif // UNIT_TESTS
1024
// --------------------------------------------------------------------------------