26 #if defined(DEVEL_RALF)
35 #define CHAR_PTR "char *"
36 #define CONST_CHAR_PTR "const char *"
42 virtual void print()
const = 0;
53 fprintf(stderr,
"arb_proto_2_xsub: Error: %s\n", error.c_str());
65 fputs(located_error.c_str(), stderr);
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());
89 setLineNumber(comment_startLineNumber);
90 throw_error(
"end of file reached while skipping comment");
100 const char *openComment,
101 const char *closeComment,
102 const char *eolComment) :
104 open_comment(openComment),
105 close_comment(closeComment),
106 eol_comment(eolComment)
112 size_t open =
line.find(open_comment);
113 size_t eol =
line.find(eol_comment);
117 if (eol != string::npos) {
120 line =
line.substr(0, open) + read_till_close_comment(
line.substr(open+2), getLineNumber());
124 if (open != string::npos) {
155 const char *brace = strpbrk(code,
"{}");
174 const char *sep_pos = strpbrk(code, separator);
189 const char *behind_sep = sep_pos + strspn(sep_pos, separator);
191 int no_of_linefeeds = 0;
192 while (code<behind_sep)
if (*++code ==
'\n') ++no_of_linefeeds;
194 *lineno += no_of_linefeeds;
204 inline char *
get_token(
const char*& code,
const char *separator) {
208 inline bool is_ID_char(
char c) {
return isalnum(c) || c ==
'_'; }
211 const char *open_paren = strchr(code,
'(');
212 const char *close_paren = strchr(code,
')');
214 if (!open_paren || (close_paren && close_paren<open_paren))
return close_paren;
221 const char *comma = strchr(code,
',');
223 const char *open_paren = strchr(code,
'(');
224 if (open_paren && open_paren<comma) {
226 if (!close_paren)
throw "Unbalanced parenthesis";
234 const char *open_paren = strchr(code,
'(');
238 opening_paren_pos = open_paren-code;
239 closing_paren_pos = close_paren-code;
247 if (type.at(type.length()-1) ==
'*')
return type+name;
248 return type+
' '+name;
256 set<string> defined_types;
263 return defined_types.find(type_decl) != defined_types.end();
285 #define TypeClass2CSTR(type) case type: return #type
286 const char *get_TypeClass_name(
TypeClass type_class) {
287 switch (type_class) {
289 TypeClass2CSTR(
VOID);
300 #undef TypeClass2CSTR
314 string from =
type2id(fromType);
316 return string(
"GBP_")+from+
"_2_"+to;
318 inline string constCastTo(
const string& expr,
const string& targetType) {
319 return string(
"const_cast<")+targetType+
">("+expr+
")";
327 string unify_type_decl(
const char *code) {
330 for (
int i = 0; code[i]; ++i) {
334 case ' ': curr = SPACE;
break;
335 case '*': curr =
STAR;
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 {
364 return conversion_function+
"("+expr+
")";
369 bool cant_handle(
const string& type_decl) {
371 strpbrk(type_decl.c_str(),
"().*") ||
372 type_decl.find(
"GB_CB") != string::npos ||
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 ||
381 type_decl.find(
"GBQUARK") != string::npos ||
382 type_decl.find(
"GB_COMPRESSION_MASK") != string::npos ||
383 type_decl.find(
"GB_CBUFFER") != string::npos ||
384 type_decl.find(
"GB_BUFFER") != string::npos;
393 c_type = unify_type_decl(code);
395 if (c_type ==
"void") { type_class =
VOID; }
397 c_type ==
"GB_ERROR" ||
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")
424 else if (cant_handle(c_type)) { type_class =
CANT_HANDLE; }
425 else if (is_forbidden(c_type)) { type_class =
FORBIDDEN; }
432 const string&
c_decl()
const {
return c_type; }
442 if (c_decl() ==
CHAR_PTR)
throw "argument of type 'char*' is forbidden";
444 return convertExpression(const_perl_arg,
CONST_CHAR_PTR, c_decl());
446 return convertExpression(perl_arg, perl_decl(), c_decl());
451 string const_c_expr = convertExpression(c_expr, c_decl(),
CONST_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));
477 static long nonameCount;
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());
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')";
518 long Parameter::nonameCount = 0;
530 void parse_arguments(
const char *arg_list) {
535 arguments.push_back(
Parameter(first_param));
538 parse_arguments(comma+1);
541 arguments.push_back(
Parameter(arg_list));
547 size_t 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());
562 ArgumentIter
args_begin()
const {
return arguments.begin(); }
563 ArgumentIter
args_end()
const {
return arguments.end(); }
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;
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;
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);
633 while (typemap_reader.
getLine(line)) {
634 if (line ==
"TYPEMAP") {
635 while (typemap_reader.
getLine(line)) {
640 defined_types.insert(c_type);
647 throw InputFileError(typemap_reader,
"Expected to see 'TYPEMAP'");
660 Package(
const char *name_,
const char *prefix_) :
663 generated_code(100000)
671 bool matches_package_prefix(
const string& text)
const {
return text.find(prefix) == 0 && text.at(prefix.length()) ==
'_'; }
678 void append_code(
const string& code) { generated_code.
ncat(code.c_str(), code.length()); }
685 fputs(
"# --------------------------------------------------------------------------------\n", file);
686 fprintf(file,
"MODULE = ARB PACKAGE = %s PREFIX = %s_\n\n", name.c_str(), prefix.c_str());
698 void generate_xsub(
const Prototype& prototype);
707 while (handcoded.
getLine(line)) {
714 size_t open_paren = line.find(
'(');
715 if (open_paren != string::npos) {
724 void generate_all_xsubs(
LineReader& prototype_reader);
733 size_t sep_offset = name.find_first_of(separator);
734 if (sep_offset != string::npos) {
735 return name.substr(0, sep_offset);
740 void xsubGenerator::generate_xsub(
const Prototype& prototype) {
742 string function_prefix =
prefix_before(c_function_name,
'_');
746 if (function_prefix ==
"GB" || function_prefix ==
"GBC") {
749 else if (function_prefix ==
"GBT" || function_prefix ==
"GEN") {
754 string perl_function_name = package->
get_prefix() + c_function_name.substr(function_prefix.length());
756 if (package->not_defined(perl_function_name)) {
757 package->mark_function_defined(perl_function_name);
763 string function_header = return_type.
isVoid() ?
"void" : return_type.
perl_decl();
765 function_header +=
'\n';
766 function_header += perl_function_name+
'('+argument_names_list+
")\n";
768 ArgumentIter arg_end = prototype.
args_end();
769 for (ArgumentIter arg = prototype.
args_begin(); arg != arg_end; ++arg) {
770 if (!arg->isVoid()) {
771 string type_decl =
string(
" ") + arg->perl_typed_param() +
'\n';
772 function_header += type_decl;
776 package->append_code(function_header);
777 package->append_linefeed();
781 string call_c_function = c_function_name+
'('+prototype.
call_arguments()+
")";
782 if (return_type.
isVoid()) {
783 package->append_code(
" PPCODE:\n ");
784 package->append_code(call_c_function);
785 package->append_code(
';');
788 string assign_RETVAL =
" ";
802 string(
" freeset(static_pntr, ") + call_c_function+
");\n"+
803 " RETVAL = static_pntr";
815 assign_RETVAL +
";\n" +
820 package->append_code(body);
822 package->append_linefeed(3);
826 fprintf(stderr,
"TRACE: '%s' skipped\n", c_function_name.c_str());
832 fprintf(stderr,
"TRACE: Skipped function: '%s' (prefix='%s')\n", c_function_name.c_str(), function_prefix.c_str());
842 bool error_occurred =
false;
844 int open_brace_counter = 0;
846 while (prototype_reader.
getLine(line)) {
847 const char *lineStart = line.c_str();
848 size_t leading_whitespace = strspn(lineStart,
" \t");
849 const char *prototype = lineStart+leading_whitespace;
855 generate_xsub(proto);
859 fprintf(stderr,
"TRACE: prototype '%s' not possible as xsub\n", prototype);
860 proto.dump_types_impossible_in_xsub(stderr);
866 error_occurred =
true;
868 catch(
const char *err) {
870 error_occurred =
true;
876 fprintf(stderr,
"TRACE: not a prototype: '%s'\n", prototype);
882 if (error_occurred)
throw ProgramError(
"could not generate xsubs for all prototypes");
887 "/* This file has been generated from\n"
899 FILE *in = fopen(filename,
"rt");
907 FILE *in = fopen(filename,
"rt");
919 Type::globalTypemap.
load(*typemap);
923 bool error_occurred =
false;
926 fputs(
"arb_proto_2_xsub converts GB_prototypes for the ARB perl interface\n"
927 "Usage: arb_proto_2_xsub <prototypes.h> <xs-header> <typemap>\n"
928 " <prototypes.h> contains prototypes of ARBDB library\n"
929 " <xs-header> may contain prototypes, which will not be\n"
930 " overwritten by generated default prototypes\n"
931 " <typemap> contains type-conversion-definitions, which can\n"
932 " be handled by xsubpp\n"
935 throw ProgramError(
"Wrong number of command line arguments");
938 const char *proto_filename = argv[1];
939 const char *xs_default_name = argv[2];
940 const char *typemap_filename = argv[3];
962 error_occurred =
true;
966 error_occurred =
true;
969 if (error_occurred) {
988 void TEST_arb_proto_2_xsub() {
991 const char *outname =
"ap2x.out";
992 const char *expected =
"ap2x.out.expected";
998 #if defined(TEST_AUTO_UPDATE)
999 TEST_COPY_FILE(outname, expected);
1008 #endif // UNIT_TESTS
string convert_argument_for_C(const string &perl_arg) const
bool possible_in_xsub() const
const string & get_name() const
int ARB_main(int argc, char **argv)
const string & perl_decl() const
ArgumentIter args_begin() const
char * get_token(const char *&code, const char *separator)
static void print_xs_default(BufferedFileReader &xs_default, const char *proto_filename, FILE *out)
long GBS_write_hash(GB_HASH *hs, const char *key, long val)
void append_code(char code)
return string(buffer, length)
bool getLine(string &line)
bool getLine_intern(string &line) OVERRIDE
TypeClass get_TypeClass() const
const char * next_comma_outside_parens(const char *code)
void mark_handcoded_functions(BufferedFileReader &handcoded)
string c_typed_param() const
void mark_function_defined(const string &function)
bool is_forward_decl(const char *code)
string convert_result_for_PERL(const string &c_expr) const
static BufferedFileReader * createFileBuffer(const char *filename)
Package(const char *name_, const char *prefix_)
static void print_prototype_parse_error(LineReader &prototype_reader, const char *err, const char *prototype)
void append_code(const string &code)
char * ARB_strdup(const char *str)
Arguments::const_iterator ArgumentIter
GB_ERROR GB_export_IO_error(const char *action, const char *filename)
bool has_definition_for(const string &type_decl) const
string type2id(const string &type)
const char * GBS_global_string(const char *templat,...)
string perl_typed_param() const
char * GBS_string_eval(const char *insource, const char *icommand)
void load(LineReader &typemap)
void GBS_free_hash(GB_HASH *hs)
void cat(const char *from)
char * ARB_strpartdup(const char *start, const char *end)
const string & get_prefix() const
vector< Parameter > Arguments
string prefix_before(const string &name, char separator)
void generate_all_xsubs(LineReader &prototype_reader)
ProgramError(string message)
static BufferedFileReader * createCommentSkippingFileBuffer(const char *filename)
string call_arguments() const
GB_ERROR GB_await_error()
const string & getFilename() const FINAL_OVERRIDE
bool contains_preprozessorCode(const char *code)
string conversion_function_name(const string &fromType, const string &toType)
void append_linefeed(size_t count=1)
static void loadTypemap(const char *typemap_filename)
const string & c_decl() const
void print_xsubs(FILE *file)
const Type & get_type() const
bool find_open_close_paren(const char *code, size_t &opening_paren_pos, size_t &closing_paren_pos)
bool not_defined(const string &function) const
bool matches_package_prefix(const string &text) const
void message(char *errortext)
static void error(const char *msg)
virtual void print() const =0
ArgumentIter args_end() const
Prototype(const char *code)
#define TEST_EXPECT_ZERO_OR_SHOW_ERRNO(iocond)
static TypeMap globalTypemap
bool contains_braces(const char *code)
bool is_prototype(const char *code)
#define TEST_EXPECT_ZERO(cond)
Parameter(const char *code)
fputs(TRACE_PREFIX, stderr)
const char * next_closing_paren(const char *code)
void append_code(const char *code)
void ncat(const char *from, size_t count)
string argument_names_list() const
bool is_typedef(const char *code)
void throw_error(int error_num, const char *error_message) __ATTR__NORETURN
string concat_type_and_name(const string &type, const string &name)
void trace_over_braces(const char *code, int &brace_counter)
string constCastTo(const string &expr, const string &targetType)
ProgramError(const char *message)
void print() const OVERRIDE
bool possible_as_xsub() const
bool ARB_strBeginsWith(const char *str, const char *with)
const char * get_data() const
#define TEST_EXPECT_TEXTFILE_DIFFLINES(fgot, fwant, diff)
bool is_empty_code(const char *code)
void print_xsubs(FILE *out)
const string & get_function_name() const
const Type & get_return_type() const
TypeClass get_TypeClass() const
long GBS_read_hash(const GB_HASH *hs, const char *key)
char * GBS_global_string_copy(const char *templat,...)
GB_HASH * GBS_create_hash(long estimated_elements, GB_CASE case_sens)
char * get_token_and_incr_lineno(const char *&code, const char *separator, size_t *lineno)
GB_write_int const char s