ARB
arb_proto_2_xsub.cxx
Go to the documentation of this file.
1 // ================================================================ //
2 // //
3 // File : arb_proto_2_xsub.cxx //
4 // Purpose : generate ARB.xs for perl interface //
5 // //
6 // ReCoded by Ralf Westram (coder@reallysoft.de) in December 2009 //
7 // Institute of Microbiology (Technical University Munich) //
8 // http://www.arb-home.de/ //
9 // //
10 // ================================================================ //
11 
12 #include <arbdb.h>
13 #include <arb_strbuf.h>
14 #include <BufferedFileReader.h>
15 
16 #include <string>
17 #include <vector>
18 #include <set>
19 
20 #include <cctype>
21 #include <unistd.h>
22 #include <arb_str.h>
23 #include <arb_diff.h>
24 
25 #if defined(DEBUG)
26 #if defined(DEVEL_RALF)
27 // #define TRACE
28 #endif // DEVEL_RALF
29 #endif // DEBUG
30 
31 using namespace std;
32 
33 // --------------------------------------------------------------------------------
34 
35 #define CHAR_PTR "char *"
36 #define CONST_CHAR_PTR "const char *"
37 
38 // --------------------------------------------------------------------------------
39 
40 struct Error {
41  virtual ~Error() {}
42  virtual void print() const = 0;
43 };
44 
45 class ProgramError : public Error {
46  string error;
47 public:
48  ProgramError(string message) : error(message) {}
49  ProgramError(const char *message) : error(message) {}
51 
52  void print() const OVERRIDE {
53  fprintf(stderr, "arb_proto_2_xsub: Error: %s\n", error.c_str());
54  }
55 };
56 
57 class InputFileError : public Error {
58  string located_error;
59 public:
60  InputFileError(LineReader& fileBuffer, string message) : located_error(fileBuffer.lineError(message)) {}
61  InputFileError(LineReader& fileBuffer, const char *message) : located_error(fileBuffer.lineError(message)) {}
63 
64  void print() const OVERRIDE {
65  fputs(located_error.c_str(), stderr);
66  fputc('\n', stderr);
67  }
68 };
69 
70 // --------------------------------------------------------------------------------
71 
73  string open_comment;
74  string close_comment;
75  string eol_comment;
76 
77  void throw_error(const char *message) __ATTR__NORETURN { throw InputFileError(*this, message); }
78 
79  string read_till_close_comment(string curr_line, size_t comment_startLineNumber) {
80  bool seen_end = false;
81  while (!seen_end) {
82  size_t close = curr_line.find(close_comment);
83  if (close != string::npos) {
84  curr_line = curr_line.substr(close+close_comment.length());
85  seen_end = true;
86  }
87  else {
88  if (!BufferedFileReader::getLine(curr_line)) {
89  setLineNumber(comment_startLineNumber);
90  throw_error("end of file reached while skipping comment");
91  }
92  }
93  }
94  return curr_line;
95  }
96 
97 public:
98  CommentSkippingFileBuffer(const string& filename_,
99  FILE *in,
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)
107  {}
108 
109 private:
110  bool getLine_intern(string& line) OVERRIDE {
112  size_t open = line.find(open_comment);
113  size_t eol = line.find(eol_comment);
114 
115  if (open != eol) { // comment found
116  if (open<eol) {
117  if (eol != string::npos) {
118  throw_error(GBS_global_string("'%s' inside '%s %s'", eol_comment.c_str(), open_comment.c_str(), close_comment.c_str()));
119  }
120  line = line.substr(0, open) + read_till_close_comment(line.substr(open+2), getLineNumber());
121  }
122  else {
123  arb_assert(eol<open);
124  if (open != string::npos) {
125  throw_error(GBS_global_string("'%s' behind '%s'", open_comment.c_str(), eol_comment.c_str()));
126  }
127  line = line.substr(0, eol);
128  }
129  }
130  return true;
131  }
132  return false;
133  }
134 };
135 
136 // --------------------------------------------------------------------------------
137 
138 inline bool is_empty_code(const char *code) { return !code[0]; }
139 inline bool contains_preprozessorCode(const char *code) { return strchr(code, '#'); }
140 inline bool contains_braces(const char *code) { return strpbrk(code, "{}"); }
141 inline bool is_typedef(const char *code) { return ARB_strBeginsWith(code, "typedef"); }
142 inline bool is_forward_decl(const char *code) { return ARB_strBeginsWith(code, "class") || ARB_strBeginsWith(code, "struct"); }
143 
144 inline bool is_prototype(const char *code) {
145  return
146  !is_empty_code(code) &&
147  !contains_preprozessorCode(code) &&
148  !contains_braces(code) &&
149  !is_typedef(code) &&
150  !is_forward_decl(code);
151 }
152 
153 inline void trace_over_braces(const char *code, int& brace_counter) {
154  while (code) {
155  const char *brace = strpbrk(code, "{}");
156  if (!brace) break;
157 
158  if (*brace == '{') {
159  ++brace_counter;
160  }
161  else {
162  arb_assert(*brace == '}');
163  --brace_counter;
164  }
165  code = brace+1;
166  }
167 }
168 
169 // --------------------------------------------------------------------------------
170 
171 inline char *get_token_and_incr_lineno(const char*& code, const char *separator, size_t *lineno) {
172  char *token = NULp;
173  if (code) {
174  const char *sep_pos = strpbrk(code, separator);
175 
176  if (!sep_pos) {
177  if (!code[0]) { // end of code
178  token = NULp;
179  code = NULp;
180  }
181  else {
182  token = ARB_strdup(code);
183  code = NULp;
184  }
185  }
186  else {
187  token = ARB_strpartdup(code, sep_pos-1);
188 
189  const char *behind_sep = sep_pos + strspn(sep_pos, separator); // next non 'separator' char
190  if (lineno) {
191  int no_of_linefeeds = 0;
192  while (code<behind_sep) if (*++code == '\n') ++no_of_linefeeds;
193 
194  *lineno += no_of_linefeeds;
195  }
196  else {
197  code = behind_sep;
198  }
199  }
200  }
201  return token;
202 }
203 
204 inline char *get_token(const char*& code, const char *separator) {
205  return get_token_and_incr_lineno(code, separator, NULp);
206 }
207 
208 inline bool is_ID_char(char c) { return isalnum(c) || c == '_'; }
209 
210 inline const char *next_closing_paren(const char *code) {
211  const char *open_paren = strchr(code, '(');
212  const char *close_paren = strchr(code, ')');
213 
214  if (!open_paren || (close_paren && close_paren<open_paren)) return close_paren;
215 
216  close_paren = next_closing_paren(open_paren+1);
217  return next_closing_paren(close_paren+1);
218 }
219 
220 inline const char *next_comma_outside_parens(const char *code) {
221  const char *comma = strchr(code, ',');
222  if (comma) {
223  const char *open_paren = strchr(code, '(');
224  if (open_paren && open_paren<comma) {
225  const char *close_paren = next_closing_paren(open_paren+1);
226  if (!close_paren) throw "Unbalanced parenthesis";
227  comma = next_comma_outside_parens(close_paren+1);
228  }
229  }
230  return comma;
231 }
232 
233 inline bool find_open_close_paren(const char *code, size_t& opening_paren_pos, size_t& closing_paren_pos) {
234  const char *open_paren = strchr(code, '(');
235  if (open_paren) {
236  const char *close_paren = next_closing_paren(open_paren+1);
237  if (close_paren) {
238  opening_paren_pos = open_paren-code;
239  closing_paren_pos = close_paren-code;
240  return true;
241  }
242  }
243  return false;
244 }
245 
246 inline string concat_type_and_name(const string& type, const string& name) {
247  if (type.at(type.length()-1) == '*') return type+name;
248  return type+' '+name;
249 }
250 
251 // ----------------
252 // TypeMap
253 
254 class TypeMap {
255  // representation of types mapped in 'typemap' file
256  set<string> defined_types;
257 
258 public:
259  TypeMap() {}
260 
261  void load(LineReader& typemap);
262  bool has_definition_for(const string& type_decl) const {
263  return defined_types.find(type_decl) != defined_types.end();
264  }
265 };
266 
267 // -------------
268 // Type
269 
270 enum TypeClass {
271  INVALID_TYPE, // invalid
272 
273  VOID, // no parameter
274  SIMPLE_TYPE, // simple types like int, float, double, ...
275  CONST_CHAR, // 'const char *'
276  HEAP_COPY, // type is 'char*' and interpreted as heap-copy
277  CONVERSION_FUNCTION, // convert type using GBP_-conversion functions
278  TYPEMAPPED, // type is defined in file 'typemap'
279 
280  CANT_HANDLE, // type cannot be used in perl interface
281  FORBIDDEN, // usage forbidden via 'NOT4PERL'
282 };
283 
284 #if defined(TRACE)
285 #define TypeClass2CSTR(type) case type: return #type
286 const char *get_TypeClass_name(TypeClass type_class) {
287  switch (type_class) {
288  TypeClass2CSTR(INVALID_TYPE);
289  TypeClass2CSTR(VOID);
290  TypeClass2CSTR(SIMPLE_TYPE);
291  TypeClass2CSTR(CONST_CHAR);
292  TypeClass2CSTR(HEAP_COPY);
293  TypeClass2CSTR(CONVERSION_FUNCTION);
294  TypeClass2CSTR(TYPEMAPPED);
295  TypeClass2CSTR(CANT_HANDLE);
296  TypeClass2CSTR(FORBIDDEN);
297  }
298  return NULp;
299 }
300 #undef TypeClass2CSTR
301 #endif // TRACE
302 
303 inline string type2id(const string& type) {
304  char *s = GBS_string_eval(type.c_str(),
305  "const =:" // remove const (for less ugly names)
306  " =:" // remove spaces
307  "\\*=Ptr"); // rename '*'
308 
309  string id(s);
310  free(s);
311  return id;
312 }
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;
317 }
318 inline string constCastTo(const string& expr, const string& targetType) {
319  return string("const_cast<")+targetType+">("+expr+")";
320 }
321 
322 class Type { // return- or parameter-type
323  string c_type;
324  string perl_type;
325  TypeClass type_class;
326 
327  string unify_type_decl(const char *code) {
328  string type_decl;
329  enum { SPACE, STAR, ID, UNKNOWN } last = SPACE, curr;
330  for (int i = 0; code[i]; ++i) {
331  char c = code[i];
332 
333  switch (c) {
334  case ' ': curr = SPACE; break;
335  case '*': curr = STAR; break;
336  default: curr = is_ID_char(c) ? ID : UNKNOWN; break;
337  }
338 
339  if (last != SPACE && curr != last) type_decl += ' ';
340  if (curr != SPACE) type_decl += c;
341 
342  last = curr;
343  }
344 
345  return last == SPACE
346  ? type_decl.substr(0, type_decl.length()-1)
347  : type_decl;
348  }
349 
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);
356  }
357  }
358 
359  string convertExpression(const string& expr, const string& fromType, const string& toType) const {
360  arb_assert(possible_in_xsub());
361  throw_if_enum();
362  if (get_TypeClass() == CONVERSION_FUNCTION) {
363  string conversion_function = conversion_function_name(fromType, toType);
364  return conversion_function+"("+expr+")"; // result is toType
365  }
366  return expr;
367  }
368 
369  bool cant_handle(const string& type_decl) {
370  return
371  strpbrk(type_decl.c_str(), "().*") || // 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;
376  }
377 
378  bool is_forbidden(const string& type_decl) {
379  return
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
385  }
386 
387 
388 public:
390 
391  Type() : type_class(INVALID_TYPE) {}
392  Type(const char *code) {
393  c_type = unify_type_decl(code);
394 
395  if (c_type == "void") { type_class = VOID; }
396  else if (c_type == CONST_CHAR_PTR ||
397  c_type == "GB_ERROR" ||
398  c_type == "GB_CSTR")
399  {
400  type_class = CONST_CHAR;
401  perl_type = CHAR_PTR;
402  }
403  else if (c_type == CHAR_PTR) {
404  type_class = HEAP_COPY;
405  perl_type = c_type;
406  }
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")
416  {
417  type_class = CONVERSION_FUNCTION;
418  perl_type = CHAR_PTR;
419  }
420  else if (globalTypemap.has_definition_for(c_type)) {
421  type_class = TYPEMAPPED;
422  perl_type = c_type;
423  }
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
426  else {
427  type_class = SIMPLE_TYPE;
428  perl_type = c_type;
429  }
430  }
431 
432  const string& c_decl() const { return c_type; }
433  const string& perl_decl() const { return perl_type; }
434 
435  TypeClass get_TypeClass() const { return type_class; }
436  bool isVoid() const { return get_TypeClass() == VOID; }
437 
438  bool possible_in_xsub() const { return type_class != CANT_HANDLE && type_class != FORBIDDEN; }
439 
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());
445  }
446  return convertExpression(perl_arg, perl_decl(), c_decl());
447  }
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);
453  }
454  return convertExpression(c_expr, c_decl(), perl_decl());
455  }
456 
457 #if defined(TRACE)
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));
462  }
463  }
464 #endif // TRACE
465 
466 };
467 
469 
470 // ------------------
471 // Parameter
472 
473 class Parameter {
474  Type type;
475  string name;
476 
477  static long nonameCount;
478 
479 public:
481  Parameter(const char *code) {
482  const char *last = strchr(code, 0)-1;
483  while (last[0] == ' ') --last;
484 
485  const char *name_start = last;
486  while (name_start >= code && is_ID_char(name_start[0])) --name_start;
487  name_start++;
488 
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());
493 
494  if (type.possible_in_xsub() && !type.isVoid() && name.empty()) {
495  name = GBS_global_string("noName%li", ++nonameCount);
496  }
497  }
498  else if (strcmp(name_start, "void") == 0) {
499  string no_type(name_start, last-name_start+1);
500  name = "";
501  type = Type(no_type.c_str());
502  }
503  else {
504  throw string("can't parse '")+code+"' (expected 'type name')";
505  }
506  }
507 
508  const string& get_name() const { return name; }
509  const Type& get_type() const { return type; }
510 
511  TypeClass get_TypeClass() const { return get_type().get_TypeClass(); }
512  bool isVoid() const { return get_TypeClass() == VOID; }
513 
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); }
516 };
517 
518 long Parameter::nonameCount = 0;
519 
520 // ------------------
521 // Prototype
522 
523 typedef vector<Parameter> Arguments;
524 typedef Arguments::const_iterator ArgumentIter;
525 
526 class Prototype {
527  Parameter function; // return-type + function_name
528  Arguments arguments;
529 
530  void parse_arguments(const char *arg_list) {
531  const char *comma = next_comma_outside_parens(arg_list);
532  if (comma) {
533  {
534  char *first_param = ARB_strpartdup(arg_list, comma-1);
535  arguments.push_back(Parameter(first_param));
536  free(first_param);
537  }
538  parse_arguments(comma+1);
539  }
540  else { // only one parameter
541  arguments.push_back(Parameter(arg_list));
542  }
543  }
544 
545 public:
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";
550  }
551 
552  string return_type_and_name(code, open_paren);
553  function = Parameter(return_type_and_name.c_str());
554 
555  string arg_list(code+open_paren+1, close_paren-open_paren-1);
556  parse_arguments(arg_list.c_str());
557  }
558 
559  const Type& get_return_type() const { return function.get_type(); }
560  const string& get_function_name() const { return function.get_name(); }
561 
562  ArgumentIter args_begin() const { return arguments.begin(); }
563  ArgumentIter args_end() const { return arguments.end(); }
564 
565  string argument_names_list() const {
566  string argument_list;
567  bool first = true;
568  ArgumentIter arg_end = arguments.end();
569 
570  for (ArgumentIter param = arguments.begin(); param != arg_end; ++param) {
571  if (!param->isVoid()) {
572  if (first) first = false;
573  else argument_list += ", ";
574 
575  argument_list += param->get_name();
576  }
577  }
578  return argument_list;
579  }
580 
581  string call_arguments() const {
582  string argument_list;
583  bool first = true;
584  ArgumentIter arg_end = arguments.end();
585 
586  for (ArgumentIter arg = arguments.begin(); arg != arg_end; ++arg) {
587  if (!arg->isVoid()) {
588  if (first) first = false;
589  else argument_list += ", ";
590 
591  argument_list += arg->get_type().convert_argument_for_C(arg->get_name());
592  }
593  }
594  return argument_list;
595  }
596 
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()) {
602  return false;
603  }
604  }
605  return true;
606  }
607  return false;
608  }
609 
610 #if defined(TRACE)
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);
616  }
617  }
618 #endif // TRACE
619 };
620 
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);
625 
626  if (trailing != string::npos) {
627  text = text.substr(leading, text.length()-leading-trailing);
628  }
629 }
630 
631 void TypeMap::load(LineReader& typemap_reader) {
632  string line;
633  while (typemap_reader.getLine(line)) {
634  if (line == "TYPEMAP") {
635  while (typemap_reader.getLine(line)) {
636  trim(line);
637  if (!line.empty()) {
638  Parameter typemapping(line.c_str());
639  const string& c_type = typemapping.get_type().c_decl();
640  defined_types.insert(c_type);
641  }
642  }
643  return;
644  }
645  }
646 
647  throw InputFileError(typemap_reader, "Expected to see 'TYPEMAP'");
648 }
649 
650 // ----------------
651 // Package
652 
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;
658 
659 public:
660  Package(const char *name_, const char *prefix_) :
661  prefix(prefix_),
662  name(name_)
663  {
664  generated_code = GBS_stropen(100000);
665  functions_to_skip = GBS_create_hash(1000, GB_MIND_CASE);
666  }
668  GBS_free_hash(functions_to_skip);
669  GBS_strforget(generated_code);
670  }
671 
672  bool matches_package_prefix(const string& text) const { return text.find(prefix) == 0 && text.at(prefix.length()) == '_'; }
673 
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; }
676 
677  const string& get_prefix() const { return prefix; }
678 
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); }
682 
683  void append_linefeed(size_t count = 1) { while (count--) append_code("\n"); }
684 
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);
689  }
690 };
691 
692 // ----------------------
693 // xsubGenerator
694 
696  Package arb;
697  Package bio;
698 
699  void generate_xsub(const Prototype& prototype);
700 public:
702  arb("ARB", "P2A"),
703  bio("BIO", "P2AT")
704  {}
705 
707  string line;
708  while (handcoded.getLine(line)) {
709  Package *package = NULp;
710 
711  if (arb.matches_package_prefix(line)) package = &arb;
712  else if (bio.matches_package_prefix(line)) package = &bio;
713 
714  if (package) {
715  size_t open_paren = line.find('(');
716  if (open_paren != string::npos) {
717  package->mark_function_defined(line.substr(0, open_paren));
718  }
719  }
720 
721  }
722  handcoded.rewind();
723  }
724 
725  void generate_all_xsubs(LineReader& prototype_reader);
726 
727  void print_xsubs(FILE *out) {
728  arb.print_xsubs(out);
729  bio.print_xsubs(out);
730  }
731 };
732 
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);
737  }
738  return "";
739 }
740 
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);
745 }
746 
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 = NULp;
751 
752  if (function_prefix == "GB" || function_prefix == "GBC") {
753  package = &arb;
754  }
755  else if (function_prefix == "GBT" || function_prefix == "GEN") {
756  package = &bio;
757  }
758 
759  if (package) {
760  string perl_function_name = package->get_prefix() + c_function_name.substr(function_prefix.length());
761 
762  if (package->not_defined(perl_function_name)) {
763  package->mark_function_defined(perl_function_name); // do NOT xsub functions twice
764 
765  // generate xsub function header
766  const Type& return_type = prototype.get_return_type();
767  {
768  string argument_names_list = prototype.argument_names_list();
769  string function_header = return_type.isVoid() ? "void" : return_type.perl_decl();
770 
771  function_header += '\n';
772  function_header += perl_function_name+'('+argument_names_list+")\n";
773 
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;
779  }
780  }
781 
782  package->append_code(function_header);
783  package->append_linefeed();
784  }
785 
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(';');
792  }
793  else {
794  string assign_RETVAL = " ";
795 
796  switch (return_type.get_TypeClass()) {
797  case CONST_CHAR:
798  case CONVERSION_FUNCTION:
799  case SIMPLE_TYPE:
800  case TYPEMAPPED:
801  assign_RETVAL = string(" RETVAL = ") + return_type.convert_result_for_PERL(call_c_function);
802  break;
803 
804  case HEAP_COPY:
805  // temporarily store heapcopy in static pointer
806  // defined at ../PERL2ARB/ARB_ext.c@static_pntr
807  assign_RETVAL =
808  string(" freeset(static_pntr, ") + call_c_function+");\n"+
809  " RETVAL = static_pntr";
810  break;
811 
812  case VOID:
813  case INVALID_TYPE:
814  case CANT_HANDLE:
815  case FORBIDDEN:
816  arb_assert(0);
817  }
818 
819  string body =
820  string(" CODE:\n") +
821  assign_RETVAL + ";\n" +
822  "\n" +
823  " OUTPUT:\n" +
824  " RETVAL";
825 
826  package->append_code(body);
827  }
828  package->append_linefeed(3);
829  }
830 #if defined(TRACE)
831  else {
832  fprintf(stderr, "TRACE: '%s' skipped\n", c_function_name.c_str());
833  }
834 #endif // TRACE
835  }
836 #if defined(TRACE)
837  else {
838  fprintf(stderr, "TRACE: Skipped function: '%s' (prefix='%s')\n", c_function_name.c_str(), function_prefix.c_str());
839  }
840 #endif // TRACE
841 }
842 
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();
845 }
846 
848  bool error_occurred = false;
849  string line;
850  int open_brace_counter = 0;
851 
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;
856 
857  if (!open_brace_counter && is_prototype(prototype)) {
858  try {
859  Prototype proto(prototype);
860  if (proto.possible_as_xsub()) {
861  generate_xsub(proto);
862  }
863 #if defined(TRACE)
864  else {
865  fprintf(stderr, "TRACE: prototype '%s' not possible as xsub\n", prototype);
866  proto.dump_types_impossible_in_xsub(stderr);
867  }
868 #endif // TRACE
869  }
870  catch(string& err) {
871  print_prototype_parse_error(prototype_reader, err.c_str(), prototype);
872  error_occurred = true;
873  }
874  catch(const char *err) {
875  print_prototype_parse_error(prototype_reader, err, prototype);
876  error_occurred = true;
877  }
878  catch(...) { arb_assert(0); }
879  }
880  else {
881 #if defined(TRACE)
882  fprintf(stderr, "TRACE: not a prototype: '%s'\n", prototype);
883 #endif // TRACE
884  trace_over_braces(prototype, open_brace_counter);
885  }
886  }
887 
888  if (error_occurred) throw ProgramError("could not generate xsubs for all prototypes");
889 }
890 
891 static void print_xs_default(BufferedFileReader& xs_default, const char *proto_filename, FILE *out) {
892  fprintf(out,
893  "/* This file has been generated from\n"
894  " * %s and\n"
895  " * %s\n */\n"
896  "\n",
897  xs_default.getFilename().c_str(),
898  proto_filename);
899 
900  xs_default.copyTo(out);
901  xs_default.rewind();
902 }
903 
904 static BufferedFileReader *createFileBuffer(const char *filename) {
905  FILE *in = fopen(filename, "rt");
906  if (!in) {
907  GB_export_IO_error("reading", filename);
908  throw ProgramError(GB_await_error());
909  }
910  return new BufferedFileReader(filename, in);
911 }
913  FILE *in = fopen(filename, "rt");
914  if (!in) {
915  GB_export_IO_error("reading", filename);
916  throw ProgramError(GB_await_error());
917  }
918  return new CommentSkippingFileBuffer(filename, in, "/*", "*/", "//");
919 }
920 
921 
922 
923 static void loadTypemap(const char *typemap_filename) {
924  SmartPtr<BufferedFileReader> typemap = createFileBuffer(typemap_filename);
925  Type::globalTypemap.load(*typemap);
926 }
927 
928 int ARB_main(int argc, char **argv) {
929  bool error_occurred = false;
930  try {
931  if (argc != 4) {
932  fputs("arb_proto_2_xsub converts GB_prototypes for the ARB perl interface\n"
933  "Usage: arb_proto_2_xsub <prototypes.h> <xs-header> <typemap>\n"
934  " <prototypes.h> contains prototypes of ARBDB library\n"
935  " <xs-header> may contain prototypes, which will not be\n"
936  " overwritten by generated default prototypes\n"
937  " <typemap> contains type-conversion-definitions, which can\n"
938  " be handled by xsubpp\n"
939  , stderr);
940 
941  throw ProgramError("Wrong number of command line arguments");
942  }
943  else {
944  const char *proto_filename = argv[1];
945  const char *xs_default_name = argv[2];
946  const char *typemap_filename = argv[3];
947 
948  loadTypemap(typemap_filename);
949 
950  // generate xsubs
951  SmartPtr<BufferedFileReader> xs_default = createFileBuffer(xs_default_name);
952 
953  xsubGenerator generator;
954  generator.mark_handcoded_functions(*xs_default);
955  {
957  generator.generate_all_xsubs(*prototypes);
958  }
959 
960  // write xsubs
961  FILE *out = stdout;
962  print_xs_default(*xs_default, proto_filename, out);
963  generator.print_xsubs(out);
964  }
965  }
966  catch (Error& err) {
967  err.print();
968  error_occurred = true;
969  }
970  catch (...) {
971  ProgramError("Unexpected exception").print();
972  error_occurred = true;
973  }
974 
975  if (error_occurred) {
976  ProgramError("failed").print();
977  return EXIT_FAILURE;
978  }
979  return EXIT_SUCCESS;
980 }
981 
982 
983 // --------------------------------------------------------------------------------
984 
985 #ifdef UNIT_TESTS
986 #ifndef TEST_UNIT_H
987 #include <test_unit.h>
988 #endif
989 #include <test_runtool.h>
990 
991 
992 // #define TEST_AUTO_UPDATE // uncomment this to update expected results
993 
994 void TEST_arb_proto_2_xsub() {
995  TEST_EXPECT_ZERO(chdir("xsub"));
996 
997  const char *outname = "ap2x.out";
998  const char *expected = "ap2x.out.expected";
999  // input/output data -> ../UNIT_TESTER/run/xsub/
1000 
1001  char *cmd = GBS_global_string_copy("arb_proto_2_xsub ptype.header default.xs typemap > %s", outname);
1002  TEST_RUN_TOOL(cmd);
1003 
1004 #if defined(TEST_AUTO_UPDATE)
1005  TEST_COPY_FILE(outname, expected);
1006 #else
1007  TEST_EXPECT_TEXTFILE_DIFFLINES(expected, outname, 0);
1008 #endif
1009  TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outname));
1010 
1011  free(cmd);
1012 }
1013 
1014 #endif // UNIT_TESTS
1015 
1016 // --------------------------------------------------------------------------------
string convert_argument_for_C(const string &perl_arg) const
#define arb_assert(cond)
Definition: arb_assert.h:245
bool possible_in_xsub() const
GB_TYPES type
const string & get_name() const
~InputFileError() OVERRIDE
int ARB_main(int argc, char **argv)
const string & perl_decl() const
const char * id
Definition: AliAdmin.cxx:17
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)
InputFileError(LineReader &fileBuffer, const char *message)
long GBS_write_hash(GB_HASH *hs, const char *key, long val)
Definition: adhash.cxx:457
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
CommentSkippingFileBuffer(const string &filename_, FILE *in, const char *openComment, const char *closeComment, const char *eolComment)
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)
~ProgramError() OVERRIDE
void append_code(const string &code)
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
Arguments::const_iterator ArgumentIter
GB_ERROR GB_export_IO_error(const char *action, const char *filename)
Definition: arb_msg.cxx:325
#define CONST_CHAR_PTR
Definition: fun.h:25
bool has_definition_for(const string &type_decl) const
string type2id(const string &type)
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:204
string perl_typed_param() const
STL namespace.
char * GBS_string_eval(const char *insource, const char *icommand)
Definition: admatch.cxx:699
void load(LineReader &typemap)
void GBS_strncat(GBS_strstruct *strstr, const char *ptr, size_t len)
Definition: arb_strbuf.cxx:101
void trim(string &text)
void GBS_free_hash(GB_HASH *hs)
Definition: adhash.cxx:541
char * ARB_strpartdup(const char *start, const char *end)
Definition: arb_string.h:51
virtual ~Error()
#define EXIT_SUCCESS
Definition: arb_a2ps.c:154
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)
GBS_strstruct * GBS_stropen(long init_size)
Definition: arb_strbuf.cxx:39
string call_arguments() const
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:353
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)
bool isVoid() const
const string & c_decl() const
#define CHAR_PTR
void print_xsubs(FILE *file)
Generic smart pointer.
Definition: smartptr.h:149
#define TEST_RUN_TOOL(cmdline)
Definition: test_runtool.h:109
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)
Definition: mkptypes.cxx:96
fputc('\n', stderr)
virtual void print() const =0
ArgumentIter args_end() const
int32_t ID
Prototype(const char *code)
#define TEST_EXPECT_ZERO_OR_SHOW_ERRNO(iocond)
Definition: test_unit.h:1079
static TypeMap globalTypemap
bool contains_braces(const char *code)
void copyTo(FILE *out)
void GBS_strforget(GBS_strstruct *strstr)
Definition: arb_strbuf.cxx:76
#define EXIT_FAILURE
Definition: arb_a2ps.c:157
bool is_prototype(const char *code)
#define TEST_EXPECT_ZERO(cond)
Definition: test_unit.h:1074
Parameter(const char *code)
TYPE get_type() const
Definition: probe_tree.h:64
void GBS_chrcat(GBS_strstruct *strstr, char ch)
Definition: arb_strbuf.cxx:119
fputs(TRACE_PREFIX, stderr)
const char * next_closing_paren(const char *code)
void append_code(const char *code)
string argument_names_list() const
bool is_typedef(const char *code)
void throw_error(int error_num, const char *error_message) __ATTR__NORETURN
Definition: util.cxx:23
char * GBS_mempntr(GBS_strstruct *strstr)
Definition: arb_strbuf.cxx:86
#define OVERRIDE
Definition: cxxforward.h:93
string concat_type_and_name(const string &type, const string &name)
Type(const char *code)
static char eol[3]
void trace_over_braces(const char *code, int &brace_counter)
string constCastTo(const string &expr, const string &targetType)
ProgramError(const char *message)
bool isVoid() const
static int line
Definition: arb_a2ps.c:296
void print() const OVERRIDE
#define NULp
Definition: cxxforward.h:97
bool is_ID_char(char c)
void print() const OVERRIDE
#define __ATTR__NORETURN
Definition: attributes.h:56
bool possible_as_xsub() const
bool ARB_strBeginsWith(const char *str, const char *with)
Definition: arb_str.h:42
#define TEST_EXPECT_TEXTFILE_DIFFLINES(fgot, fwant, diff)
Definition: test_unit.h:1388
bool is_empty_code(const char *code)
void print_xsubs(FILE *out)
void GBS_spaces(GBS_strstruct *out, int space_count)
const string & get_function_name() const
InputFileError(LineReader &fileBuffer, string message)
const Type & get_return_type() const
TypeClass get_TypeClass() const
long GBS_read_hash(const GB_HASH *hs, const char *key)
Definition: adhash.cxx:395
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:195
GB_HASH * GBS_create_hash(long estimated_elements, GB_CASE case_sens)
Definition: adhash.cxx:253
void print(const T &t)
Definition: test_unit.h:348
char * get_token_and_incr_lineno(const char *&code, const char *separator, size_t *lineno)
GB_write_int const char s
Definition: AW_awar.cxx:156