~hwkrus/f03gl/trunk

1 by Henk Krus
Initial Launchpad setup
1
#!/usr/bin/perl -w
2
55 by dolfyn
August 2013 version of h_interfaces.pl
3
#  Copyright 2009 Anthony Stone and Aleksandar Donev
4
5
#  This file is part of f03gl.
6
#
7
#  f03gl is free software: you can redistribute it and/or modify
8
#  it under the terms of the GNU General Public License as published by
9
#  the Free Software Foundation, either version 3 of the License, or
10
#  any later version.
11
#
12
#  f03gl is distributed in the hope that it will be useful,
13
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
#  GNU General Public License for more details.
16
#
17
#  You should have received a copy of the GNU General Public License
18
#  along with f03gl (see file COPYING). If not, see
19
#  <http://www.gnu.org/licenses/>.
20
21
1 by Henk Krus
Initial Launchpad setup
22
#  Construct a file of Fortran interfaces for the GLUT parameters and
23
#  procedures, using the GLUT.h header file as the starting-point.
24
25
$verbose=1;
26
$private=1;
27
$infile="OpenGLUT.h";
28
$module="f03gl";
29
$bozinit=0;
30
$no_dimension=0;
55 by dolfyn
August 2013 version of h_interfaces.pl
31
$command_line="$0 ".join(" ",@ARGV);
1 by Henk Krus
Initial Launchpad setup
32
while (@ARGV) {
33
  $flag=shift;
34
  $verbose=0, next if $flag eq "-q";
35
  $verbose=1, next if $flag eq "-v";
36
  $private=1, next if $flag eq "--private";
37
  $infile=shift, next if $flag eq "-i";
38
  $bozinit=1, next if $flag eq "--bozinit";
39
  $no_dimension=1, next if $flag eq "--scalar";
40
  $module=shift, next if $flag eq "-m" or $flag eq "--module";
41
  &help, exit if $flag eq "--help";
42
  print "$flag not recognised\n";
43
  &help; exit;
44
}
45
46
#  Lookup table of Fortran equivalents to C argument types
47
%map = (
48
int        => "INTEGER(GLint)",
49
short      => "INTEGER(C_SHORT)",
50
float      => "REAL(C_FLOAT)",
51
double     => "REAL(C_DOUBLE)",
52
"unsigned int" => "INTEGER(GLuint)",
55 by dolfyn
August 2013 version of h_interfaces.pl
53
"char"   =>  "CHARACTER(C_CHAR)",
1 by Henk Krus
Initial Launchpad setup
54
"char**"  =>  "TYPE(C_PTR), INTENT(IN)",
55
"unsigned char" => "INTEGER(GLbyte)",
55 by dolfyn
August 2013 version of h_interfaces.pl
56
"unsigned char*" => "CHARACTER, DIMENSION(*)",
1 by Henk Krus
Initial Launchpad setup
57
GLint      => "INTEGER(GLint)",
58
GLuint     => "INTEGER(GLuint)",
59
GLenum     => "INTEGER(GLenum)",
60
GLboolean  => "INTEGER(GLboolean)",
61
GLbitfield => "INTEGER(GLbitfield)",
62
GLbyte     => "INTEGER(GLbyte)",
63
GLubyte    => "INTEGER(GLubyte)",
64
GLshort    => "INTEGER(GLshort)",
65
GLushort   => "INTEGER(GLushort)",
66
GLsizei    => "INTEGER(GLsizei)",
67
GLfloat    => "REAL(GLfloat)",
68
GLdouble   => "REAL(GLdouble)",
69
GLclampf   => "REAL(GLclampf)",
70
GLclampd   => "REAL(GLclampd)",
71
"void*"   => "TYPE(C_PTR)",
72
"void(*)" => "TYPE(C_FUNPTR)",
73
GLUTproc => "TYPE(C_FUNPTR)"
74
);
75
76
if ($private) {
77
  $ACCESS="PRIVATE";
78
  $PARAMETER="PARAMETER, PUBLIC";
79
  $PUBLIC=", PUBLIC";
80
}
81
else {
82
  $ACCESS="PUBLIC";
83
  $PARAMETER="PARAMETER";
84
  $PUBLIC="";
85
}
86
55 by dolfyn
August 2013 version of h_interfaces.pl
87
open (STDIN,$infile) or die "Can't open $infile";
88
open (STDOUT,">${module}_glut.f90") or die "Can't open ${module}_glut.f90 for output";
1 by Henk Krus
Initial Launchpad setup
89
90
print "MODULE ${module}_glut
55 by dolfyn
August 2013 version of h_interfaces.pl
91
92
!  Derived from $infile using
93
!  $command_line
94
1 by Henk Krus
Initial Launchpad setup
95
USE ${module}_kinds
96
IMPLICIT NONE
97
$ACCESS
98
99
";
100
101
$contained_wrappers="";
102
103
while (<>) {
104
  if ( /^#ifdef __cplusplus/ ) {
105
    #  Strip everything up to #endif (crude!)
106
    while (<>) {
107
      last if /^#endif/;
108
    }
109
  }
110
  if ( /^\s*$/) {
111
    #  Copy blank lines
112
    print "\n";
113
  }
114
  elsif ( m+^/\* *(.*) *\*/$+ ) {
115
    #  Copy one-line comments in Fortran form
116
    print "!  $1\n";
117
  }
118
  elsif ( m+^/\*+ ) {
119
    while (<>) {
120
      #  Copy multi-line comments
121
      last if m+\*/+;
122
      s/^ *\* *//;
123
      print "!  "; print;
124
    }
125
  }
126
  elsif ( /^#define +(\w+)\s+(.+)$/ ) {
127
    #  Parameter values
128
    $name=$1;
129
    $value=$2;
130
    if ( $value =~ /^0x(\w*)/ ) {
131
      if ( $bozinit ) {
132
        #  Convert to Fortran z'...' form
133
        $value=~s/0x(\w+)/z'$1'/;
134
      }
135
      else {
136
        #  Convert to decimal integer
137
        $value=oct($value);
138
      }
139
      print "INTEGER(GLenum), $PARAMETER :: $name = $value\n";
140
    }
141
    elsif ( $value =~ /^(\w+)$/ )  {
142
      print "INTEGER(GLenum), $PARAMETER :: $name = $value\n";
143
    }
144
    else {
145
      #  Unknown #define
146
      print "! ???, $PUBLIC :: $name=$value\n";
147
      # print "TYPE(C_PTR), BIND(C), PROTECTED$PUBLIC :: $name\n";
148
    }
149
  }
150
  elsif ( s/^ *extern +// || s/^\w*API +// ) {
151
    #  Is it a procedure definition?
152
    if ( /\w+APIENTRY/) {
153
      #  Make sure we have the whole thing
154
      while ( !/;$/ ) {
155
	chomp;
156
	$_.=<>;
157
      }
158
      #  Pass to subroutine with initial "extern" or "GLUTAPI" or "OGAPI"
159
      #  or "FGAPI" keyword now stripped
160
      $depth=0;
161
      $callback_wrapper=0; # Automatically generate the wrapper for glut callback functions
162
      &procdef($_);
163
      print "\n";
164
    }
165
  }
166
  else {
167
    #  Ignore
168
  }
169
}
170
171
print "END INTERFACE\n\n" unless $private;
172
173
# Manual handling
174
print "
175
! Font variables in GLUT_fonts.c
176
TYPE(C_PTR), BIND(C), PUBLIC, PROTECTED :: GLUT_STROKE_ROMAN,         &
177
    GLUT_STROKE_MONO_ROMAN, GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, &
178
    GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24,           &
179
    GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12,               &
180
    GLUT_BITMAP_HELVETICA_18
181
182
! A special callback function for compatibility with f90gl
55 by dolfyn
August 2013 version of h_interfaces.pl
183
TYPE(C_FUNPTR), PUBLIC, SAVE :: GLUT_NULL_FUNC=C_NULL_FUNPTR
1 by Henk Krus
Initial Launchpad setup
184
185
CONTAINS
186
187
SUBROUTINE glutInit_f03()
188
  INTEGER(C_INT) :: argcp=1
189
  TYPE(C_PTR), DIMENSION(1), TARGET :: argv=C_NULL_PTR
190
  CHARACTER(C_CHAR), DIMENSION(1), TARGET :: empty_string=C_NULL_CHAR
191
192
  ! A hack
193
  INTERFACE
194
   SUBROUTINE SetNullFunc() BIND(C,NAME='SetNullFunc')
195
   END SUBROUTINE
196
  END INTERFACE  
197
198
  argv(1)=C_LOC(empty_string)
199
  CALL glutInit_gl(argcp, C_LOC(argv))
200
  
201
END SUBROUTINE
202
203
$contained_wrappers
204
";
205
206
print "
207
END MODULE ${module}_glut
208
209
";
210
211
sub procdef {
212
  #  Procedure definition
213
  my $c_defn=$_[0];
214
  %declare=();
215
  local %interface=();
216
  my $procname="";
217
  my $proctype="";
218
  my $wrapper=0; # No wrapper--direct interface
219
  my $my_args="";
220
  
221
  $depth++;
222
  # return if $c_defn=~/^void *\*\w+;/;
223
  $c_defn=~s/\w*APIENTRY *//;  #  remove OGAPIENTRY etc.
224
  #  $c_defn=~s+ */\*.*\*/++;
225
  $c_defn=~s/ *; *$//;  #  Remove trailing semicolon
226
  print "!  $c_defn";
227
  ($return,$star,$procname,$param_list)=($c_defn=~/^(\w+) *(\*?|\(\*\)) *(\w+) *\((.*)\)$/);
228
  print STDERR "\n$procname\($param_list\)\n" if $verbose;
229
  ($return_type,$proctype)=&handle_return($return,$star);
230
  &handle_params($param_list);
231
  $my_args=$args; # Save a local copy of the argument list
232
  
233
  print "PUBLIC $procname\n" if $depth==1;
234
  
235
  if ( $depth==1 ) {
236
     if ( $procname =~ /glut\w*Func/ && $procname !~ /glutForceJoystickFunc/ ) 
237
     {
238
       print STDERR "Generating wrapper for $procname!\n";
239
       $callback_wrapper=1;
240
       $wrapper=1;
241
     }
242
     elsif ( $procname =~ /glutInit\b/ ) {
243
       $wrapper=1; # We supply a manual wrapper for this one
244
     }
245
  }
246
  
247
  if($callback_wrapper and ($depth>1)) {
248
     $callback_name=$procname;
249
     print "TYPE(C_FUNPTR), VALUE :: $procname\n";
250
     $contained_wrappers.="INTERFACE\n";
251
     $contained_wrappers.="$proctype $procname($args) BIND(C)\nIMPORT\n";
252
  }  
253
  else
254
  {
255
     if($wrapper) {
256
       print "INTERFACE ${procname}\n"; # The generic wrapper
257
       print "MODULE PROCEDURE ${procname}_f03\n";
258
       print "END INTERFACE ${procname}\n";
259
       print "INTERFACE\n"; # The C GLUT function
260
       print "$proctype ${procname}_gl($args) ";    
261
     }
262
     else {
263
       print "INTERFACE\n";
264
       print "$proctype $procname($args) ";
265
     }
266
     if ( $depth==1 ) {
267
       print qq+BIND(C,NAME="$procname")\nIMPORT\n+;
268
     }
269
     else {
270
       print "BIND(C)\nIMPORT\n";
271
     }
272
  }  
273
  if($callback_wrapper and ($depth==1)) { $contained_wrappers.="$proctype ${procname}_f03($args)\n"; }
274
  
275
  
276
  if ($proctype eq "FUNCTION") {
277
     if (not ($callback_wrapper and ($depth>1))) {
278
        print "$return_type :: $procname\n" ; 
279
     }
280
     if($callback_wrapper) { $contained_wrappers.="$return_type :: $procname\n"; }
281
  }   
282
     
283
  foreach (sort keys %declare) {
284
    $vars=$declare{$_};
285
    if (/DIMENSION\(\*\)/) {
286
      $dummy=$_;
287
      if ( $no_dimension && !/^CHARACTER/ ) {
288
	s/, *DIMENSION\(\*\)//;
289
      }
290
      else {
291
	$dummy=~s/, *DIMENSION\(\*\)//;
292
      }
293
      print "! $dummy :: $vars\n";      
294
    }
295
    if (not ($callback_wrapper and ($depth>1))) { print "$_ :: $vars\n"; }
296
    if($callback_wrapper) { $contained_wrappers.="$_ :: $vars\n"; }
297
  }
298
  
299
  for (keys %interface) {
300
    # print STDERR "$interface{$_}\n" if $verbose;
301
    &procdef($interface{$_});
302
  }
303
  
304
  if ($callback_wrapper and ($depth>1)) {
305
    $contained_wrappers.="END $proctype ${procname}\n";
306
    $contained_wrappers.="END INTERFACE\n";
307
  }
308
  else
309
  {
310
     if($wrapper) {
311
       print "END $proctype ${procname}_gl\n";
312
     }
313
     else {
314
       print "END $proctype $procname\n";
315
     }
316
     print "END INTERFACE\n";
317
  }
318
    
319
  if($callback_wrapper and ($depth==1)) {
320
     $c_loc_args=$my_args;
321
     $c_loc_args=~s/$callback_name/C_FUNLOC($callback_name)/;
322
     $c_null_args=$my_args;
323
     $c_null_args=~s/$callback_name/C_NULL_FUNPTR/;
324
     $contained_wrappers.=
325
"OPTIONAL :: $callback_name
326
IF(PRESENT($callback_name)) THEN
327
   CALL ${procname}_gl($c_loc_args)
328
ELSE
329
   CALL ${procname}_gl($c_null_args)
330
END IF
331
END $proctype ${procname}_f03\n";
332
  }
333
  $depth--;
334
}
335
336
sub handle_return {
337
  my $s=$_[0];
338
  my $star=$_[1];
339
  $s=~s/ +$//;
340
  if ($s eq "void" && $star eq "") {
341
    $proctype="SUBROUTINE";
342
    $return_type="";
343
  }
344
  else {
345
    $proctype="FUNCTION";
346
    $type=$s.$star;
347
    if ( ! defined($map{$type}) ) {
348
      print STDERR "return type $type not defined\n";
349
      $map{$type}="*** $type";
350
    }
351
    $return_type=$map{$type};
352
    #  VALUE attribute not possible for function result
353
    $return_type=~s/, VALUE//;
354
  }
355
  ($return_type,$proctype);
356
}
357
358
sub handle_params {
359
  my $param_string=$_[0];
360
  my $variable="";
361
  my $qualifiers="";
362
  my $intent="";
363
  my $value=1;
364
  $args="";
365
  my $a=0;
366
  #  Cycle while $param_string non-blank
367
  while ( $param_string!~/^\s*$/ ) {
368
    $variable="";
369
    $qualifiers="";
370
    $intent="";
371
    $value=1;
372
    last if $param_string=~/^ *void *$/;  #  Subroutine with no parameters
373
    $type="";
374
    while ( 1 ) {
375
      #  Extract type part of parameter specification
376
      ($word,$param_string)=($param_string=~/(\w+)\b *(.*)/);
377
      if ( $word eq "const" ) {
378
	$intent=", INTENT(IN)";
379
	$value=0;
380
	next;
381
      }
382
      elsif ( $word eq "unsigned" ) {
383
	#  Append to type string and loop
384
	$type.="$word ";
385
	next;
386
      }
387
      #  Otherwise append to type string and proceed
388
      $type.=$word;
389
      last;
390
    }
391
    #  Look for * or ** or (*) or (* callback); strip off if present.
392
    if ( $param_string=~s/^(\*+|\(\*( callback)?\)) *//i ) {
393
      $tag=$1;
394
      if ( $tag eq "(*)" || $tag=~/\(\* *callback\)/i ) {
395
	#  (*) or (* callback) means an un-named procedure -- give it a name ...
396
	$variable="proc";
397
	#  Remember its name and return type
398
	$interface{$variable}="$type $variable";
399
	#  ... and attach its arguments
400
	$param_string=~s/(\([^\)]+\))//;
401
	$interface{$variable}.="$1\n";
402
	$type="";
403
	print STDERR "$interface{$variable}\n" if $verbose;
404
      }
405
      elsif ( $type eq "char" && $tag eq "**" || $type eq "unsigned char"
406
	      || $type eq "void" ) {
407
        #  Append to type string
408
	$value=0 unless $type eq "void";
409
	$type.="$1";
410
      }
411
      else {
412
	$qualifiers.=", DIMENSION(*)";
413
	$value=0;
414
      }
415
    }
416
    elsif ( $param_string=~s/^\(\w*(CALLBACK)? *\*(\w+)\)//i ) {
417
      #  Callback procedure; the word is its name
418
      $variable=$2;
419
      $interface{$variable}="$type $variable";
420
      #  Attach its arguments
421
      $param_string=~s/(\([^\)]+\))//;
422
      $interface{$variable}.="$1\n";
423
      $type="";
424
      print STDERR "$interface{$variable}\n" if $verbose;
425
    }
426
    #  The parameter name is the next word, unless we have already
427
    #  assigned a name
428
    if (  $variable eq "" ) {
429
      if ($param_string=~/^(\w+)(.*)/) {
430
	($variable,$param_string)=($param_string=~/^(\w+)(.*)/) if $variable eq "";
431
      }
432
      else {
433
	#  No parameter name provided (usually because this is an
434
	#  argument of a callback). Invent one.
435
	$a++;
436
	$variable="arg$a";
437
      }
438
    }
439
    #  If [dimen] follows, it is an array dimension
440
    if ( $param_string=~s/^\[(.*?)\]// ) {
441
      $qualifiers=", DIMENSION($1)";
442
      $value=0;
443
    }
444
    #  If a comma follows, this is not the last parameter. Extract
445
    #  the separator.
446
    if ( $param_string=~s/^, *// ) {
447
      $sep=", ";
448
    }
449
    else {
450
      $sep="";
451
    }
452
453
    if ($value) {
454
      $qualifiers.=", VALUE";
455
    }
456
    else {
457
      $qualifiers.=$intent;
458
    }
459
460
    #  Append the argument name and separator to the argument string
461
    $args.="$variable$sep";
462
    #  Get the Fortran declaration for this parameter type from the
463
    #  lookup table.
464
    if ( $type ) {
465
      if ( defined($map{$type}) ) {
466
	$param_type=$map{$type};
467
	if ( defined($declare{"$param_type$qualifiers"}) ) {
468
	  #  Add this variable to an existing declaration
469
	  $declare{"$param_type$qualifiers"}.=", $variable";
470
	}
471
	else {
472
	  #  New declaration
473
	  $declare{"$param_type$qualifiers"}="$variable";
474
	}
475
      }
476
      else {
477
	print STDERR "Parameter type $type unknown\n";
478
      }
479
    }
480
  }
481
}
482
483
sub help {
484
  print qq+
485
Usage: $0 [-v | -q] [-i inputfile] [-m module | --module module] [--bozinit] [--scalar]
486
Translate the inputfile (default OpenGLUT.h) into a Fortran 2003 module
55 by dolfyn
August 2013 version of h_interfaces.pl
487
file.
488
489
The -m or --module option gives the base name of the module file.
490
The default is f03gl, so that the full name becomes f03gl_glut.f90.
1 by Henk Krus
Initial Launchpad setup
491
492
-v (verbose) gives more output, -q suppresses it.
493
494
--bozinit :
495
hexadecimal parameter values are left in hex
496
but expressed in Fortran BOZ notation, i.e. z'...' instead of 0x...
497
Otherwise they are converted to decimal. This is not the default, since
498
BOZ constants are not allowed in initialisation expressions in standard
499
Fortran 2003, but some compilers accept them.
500
501
--scalar:
502
C declarations such as "int* v" describe a pointer to an entity v
503
which may be a scalar or an array. In Fortran it is necessary to
504
specify which is required. The attribute "DIMENSION(*)" is provided
505
unless this flag is present. In either case the alternative form is
506
also provided but is commented out. Exception: char* is always
55 by dolfyn
August 2013 version of h_interfaces.pl
507
translated to "CHARACTER(C_CHAR), DIMENSION(*)".
1 by Henk Krus
Initial Launchpad setup
508
+;
509
}