rubber-generate 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +12 -1
- data/lib/rubber/codegen/array.rb +59 -0
- data/lib/rubber/codegen/function.rb +6 -3
- data/lib/rubber/codegen.rb +10 -2
- data/lib/rubber/scanner.rb +82 -9
- metadata +4 -3
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. Rubber Generate
|
2
2
|
|
3
|
-
h2. v0.0.
|
3
|
+
h2. v0.0.7
|
4
4
|
|
5
5
|
Template language for generating Ruby bindings for C libraries
|
6
6
|
by Geoff Youngs <g@intersect-uk.co.uk>
|
@@ -18,6 +18,17 @@ it is planned to remove this dependency unless they genuinely require Ruby/GTK.
|
|
18
18
|
Other features include custom named type-maps, pre/post code inclusion within
|
19
19
|
functions and some rudimentary understanding of C code.
|
20
20
|
|
21
|
+
h3. Changes
|
22
|
+
|
23
|
+
* 0.0.7 - 7th May 2010
|
24
|
+
- Add support for array constants (including autmatic type detection) - e.g array <ConstantName> = [ "Text", NULL, 3 ]
|
25
|
+
- Add syntax_error function to parser which reports slightly more helpful syntax error messages for a .cr file
|
26
|
+
- Add tracking of code location so that C compiler throws errors which refer to the original line in the .cr file
|
27
|
+
- Add support for GSList{type} or GList{type} conversion to arrays as return values for functions
|
28
|
+
|
29
|
+
* 0.0.6 - 6th May 2010
|
30
|
+
- Flags class allows wrapping a bit field with slightly more information. Plus automatic conversion to/from ruby and support for integer values.
|
31
|
+
|
21
32
|
h3. Dependencies
|
22
33
|
|
23
34
|
* Ruby 1.8.6
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Rubber
|
2
|
+
|
3
|
+
class C_Array
|
4
|
+
define_members :name, :values, :parent
|
5
|
+
def code(io)
|
6
|
+
end
|
7
|
+
def declare(io)
|
8
|
+
#io.puts "static VALUE #{cname};"
|
9
|
+
end
|
10
|
+
include RegisterChildren
|
11
|
+
def default_cname
|
12
|
+
#"enum"+name
|
13
|
+
end
|
14
|
+
def doc_rd(io)
|
15
|
+
depth = (fullname.gsub(/[^:]/,'').size >> 1)
|
16
|
+
io.puts "=#{'=' * depth} #{fullname}"
|
17
|
+
end
|
18
|
+
def get_root(); is_root? ? self : parent.get_root; end; def is_root?()
|
19
|
+
not parent.respond_to?(:fullname)
|
20
|
+
end
|
21
|
+
def fullname()
|
22
|
+
if parent and parent.respond_to?(:fullname)
|
23
|
+
"#{parent.fullname}::#{name}"
|
24
|
+
else
|
25
|
+
name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
def get_root(); is_root? ? self : parent.get_root; end;
|
29
|
+
def is_root?()
|
30
|
+
not parent.respond_to?(:fullname)
|
31
|
+
end
|
32
|
+
def register(io, already_defined=false)
|
33
|
+
if parent
|
34
|
+
args = "#{@values.size}"
|
35
|
+
@values.each_with_index do |hash, index|
|
36
|
+
args << ", "
|
37
|
+
case hash.keys[0]
|
38
|
+
when :int
|
39
|
+
args << "INT2NUM(#{hash.values[0]})"
|
40
|
+
when :bool
|
41
|
+
args << "((#{hash.values[0]}) ? Qtrue : Qfalse)"
|
42
|
+
when :float
|
43
|
+
args << "FLOAT2NUM(#{hash.values[0]})"
|
44
|
+
when :string
|
45
|
+
args << "rb_str_new2(#{hash.values[0]})"
|
46
|
+
when :nil
|
47
|
+
args << "Qnil"
|
48
|
+
else
|
49
|
+
raise "Unknown key type for static array - #{hash.keys[0]}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
io.puts " rb_define_const(#{parent.cname}, #{name.inspect}, rb_ary_new3(#{args}));"
|
53
|
+
else
|
54
|
+
raise "No parent for string constant #{name}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end # Rubber
|
@@ -2,6 +2,7 @@ module Rubber
|
|
2
2
|
|
3
3
|
class C_Function
|
4
4
|
define_members(:name, :args, :text, :parent, {:autofree=>[]}, {:returntype=>'VALUE'}, :doc=>'')
|
5
|
+
attr_accessor :source_line, :source_file
|
5
6
|
attr_reader :multi, :block, :rest, :singleton
|
6
7
|
def check()
|
7
8
|
return if @checked
|
@@ -136,7 +137,9 @@ class C_Function
|
|
136
137
|
end
|
137
138
|
}
|
138
139
|
end
|
140
|
+
|
139
141
|
io.puts ""
|
142
|
+
io.puts "#line #{source_line} #{source_file.inspect}" if source_line
|
140
143
|
setupvars = io.string
|
141
144
|
io = oio
|
142
145
|
|
@@ -153,7 +156,7 @@ class C_Function
|
|
153
156
|
to_type = (!(to_type.nil? or to_type.empty?) && to_type || (arg && arg.ctype || guess(cast)))
|
154
157
|
io.write(Rubber.explicit_cast(cast, from_type, to_type))
|
155
158
|
elsif txt = sc.scan(CAST)
|
156
|
-
|
159
|
+
warn("<TYPE:VALUE> is deprecated - please use <{FROM_TYPE>TO_TYPE:VALUE}> instead.")
|
157
160
|
name, cast = sc[1], sc[2]
|
158
161
|
arg = @arghash[name]
|
159
162
|
io.write(Rubber::explicit_cast(name, arg && arg.ctype || guess(name), cast))
|
@@ -187,8 +190,8 @@ class C_Function
|
|
187
190
|
retval << (mini_scanner.get_byte)
|
188
191
|
end
|
189
192
|
end
|
190
|
-
unless Rubber
|
191
|
-
io << Rubber
|
193
|
+
unless Rubber.native_type?(returntype)
|
194
|
+
io << Rubber.explicit_cast(retval, returntype, 'VALUE')
|
192
195
|
else
|
193
196
|
io << retval
|
194
197
|
end
|
data/lib/rubber/codegen.rb
CHANGED
@@ -18,9 +18,14 @@ def generate_c_source(scanner, io)
|
|
18
18
|
io.puts "typedef int rubber_bool;"
|
19
19
|
io.puts "#define bool rubber_bool"
|
20
20
|
io.puts "\n/* Prototypes */"
|
21
|
-
|
21
|
+
if scanner.options.glib?
|
22
|
+
io.puts <<-EOGLIB
|
23
|
+
#include "rbglib.h"
|
24
|
+
|
25
|
+
EOGLIB
|
26
|
+
|
22
27
|
|
23
|
-
if scanner.options.
|
28
|
+
if scanner.options.gtk?
|
24
29
|
io.write <<-EOI
|
25
30
|
#include "rbgtk.h"
|
26
31
|
|
@@ -40,6 +45,7 @@ RUBY_GTK2_VAR VALUE mGdk;
|
|
40
45
|
#define RBGTK_INITIALIZE(obj,gtkobj)\
|
41
46
|
(rbgtk_initialize_gtkobject(obj, GTK_OBJECT(gtkobj)))
|
42
47
|
EOI
|
48
|
+
end
|
43
49
|
end
|
44
50
|
|
45
51
|
|
@@ -69,6 +75,7 @@ end
|
|
69
75
|
module_function :generate_c_source
|
70
76
|
|
71
77
|
module RegisterChildren
|
78
|
+
attr_accessor :source_line, :source_file
|
72
79
|
attr_reader :child_names
|
73
80
|
def register_children(io)
|
74
81
|
@child_names = {}
|
@@ -112,6 +119,7 @@ require 'rubber/codegen/function'
|
|
112
119
|
require 'rubber/codegen/string'
|
113
120
|
require 'rubber/codegen/integer'
|
114
121
|
require 'rubber/codegen/float'
|
122
|
+
require 'rubber/codegen/array'
|
115
123
|
|
116
124
|
# Special
|
117
125
|
require 'rubber/codegen/struct'
|
data/lib/rubber/scanner.rb
CHANGED
@@ -33,7 +33,9 @@ def initialize(file)
|
|
33
33
|
@options.glib= true
|
34
34
|
@options.gtk= true
|
35
35
|
@options.gnu= false
|
36
|
+
@current_file = file
|
36
37
|
end
|
38
|
+
attr_reader :current_file
|
37
39
|
def scan_args()
|
38
40
|
args = []
|
39
41
|
return args unless @str.peep(1) == '('
|
@@ -76,7 +78,7 @@ def scan(fp)
|
|
76
78
|
end
|
77
79
|
def _scan(fp)
|
78
80
|
@lines = IO.readlines(@file)
|
79
|
-
@str = StringScanner.new(@lines.join)
|
81
|
+
@str = StringScanner.new(@string = @lines.join)
|
80
82
|
tokens = []
|
81
83
|
@state = ScanState.new(0,0,false,0)
|
82
84
|
@stack = [C_RootModule.new]
|
@@ -129,7 +131,7 @@ def _scan(fp)
|
|
129
131
|
when 'glib','gtk','gnu'
|
130
132
|
@options[@str[1]] = (@str[2] == 'yes')
|
131
133
|
else
|
132
|
-
|
134
|
+
syntax_error "Unknown option #{@str[1]}"
|
133
135
|
end
|
134
136
|
elsif @str.skip(/%lib\s+(.+)\n/) # Skip single-line comment
|
135
137
|
@libs ||= []
|
@@ -152,7 +154,7 @@ def _scan(fp)
|
|
152
154
|
if stack.last.respond_to?(flag)
|
153
155
|
stack.last.__send__(flag, true)
|
154
156
|
else
|
155
|
-
|
157
|
+
syntax_error "%flags directive cannot be used here (#{stack.last.class} doesn't respond to #{flag})"
|
156
158
|
end
|
157
159
|
elsif state.in_class > 0 and state.in_func == false and @str.skip(/%feature\s+([a-z0-9_A-Z]+)/)
|
158
160
|
flag = ("feature_"+@str[1]).intern
|
@@ -165,7 +167,7 @@ def _scan(fp)
|
|
165
167
|
if stack.last.respond_to?(flag)
|
166
168
|
stack.last.__send__(flag, *args)
|
167
169
|
else
|
168
|
-
|
170
|
+
syntax_error "%feature '#{@str[1]}' directive cannot be used here (#{stack.last.class} doesn't support it)"
|
169
171
|
end
|
170
172
|
elsif @str.skip(/%/) # Skip single-line comment
|
171
173
|
@str.skip_until(/\n/)
|
@@ -286,7 +288,7 @@ def _scan(fp)
|
|
286
288
|
when "gboxed"
|
287
289
|
@classes[name] = C_GBoxed.new(name, gtype, [], [], [])
|
288
290
|
else
|
289
|
-
|
291
|
+
syntax_error "#{name} is not a GObject or GInterface..."
|
290
292
|
end
|
291
293
|
@classes[name].gparent_class = gparent_class
|
292
294
|
@classes[name].parent = stack.last
|
@@ -324,7 +326,7 @@ def _scan(fp)
|
|
324
326
|
STDERR.puts "Remaining code: #{@str.rest}"
|
325
327
|
STDERR.puts "Defined Classes: #{@classes.keys.join(', ')}"
|
326
328
|
p stack
|
327
|
-
|
329
|
+
syntax_error "Invalid stack entry #{last.class}"
|
328
330
|
end
|
329
331
|
elsif @str.skip(/alias\s+:([A-Za-z0-9_]*[\[\]]{0,2}[?!=]?)\s+:([A-Za-z0-9_]*[\[\]]{0,2}[?!=]?)/) # Alias
|
330
332
|
@class.add_alias(@str[1], @str[2]) if @class.respond_to?(:add_alias)
|
@@ -332,7 +334,7 @@ def _scan(fp)
|
|
332
334
|
where = @str[1]
|
333
335
|
str = @str.scan_until(/\bend/)#[0,-4]
|
334
336
|
unless str
|
335
|
-
|
337
|
+
syntax_error "Invalid #{where}_func definition"
|
336
338
|
end
|
337
339
|
str = str[0..-4].strip
|
338
340
|
except = only = nil
|
@@ -361,6 +363,30 @@ def _scan(fp)
|
|
361
363
|
@str.skip(/\s+/)
|
362
364
|
if @str.scan(/([a-zA-Z_* ]+):/)
|
363
365
|
returntype = @str[1]
|
366
|
+
elsif @str.skip(/(GS?List){([^}]+)}:/)
|
367
|
+
container = @str[1]
|
368
|
+
ct = @str[2]
|
369
|
+
cn = ct.gsub(/\s+/,'').gsub(/[*]/,'__p')
|
370
|
+
rule = container+'{'+ct+'}'
|
371
|
+
returntype = rule
|
372
|
+
|
373
|
+
#syntax_error "Auto converting (#{rule}) GSList of #{ct} is not yet supported"
|
374
|
+
# sane
|
375
|
+
unless $custom_maps[rule] && $custom_maps[rule]['VALUE']
|
376
|
+
function = "rubber_#{container}_of_#{cn}_to_array"
|
377
|
+
@raw = @raw.to_s + <<-EOADD
|
378
|
+
inline VALUE #{function}(#{container} *list) {
|
379
|
+
#{container} *p; volatile VALUE ary;
|
380
|
+
ary = rb_ary_new();
|
381
|
+
for(p = list ; p ; p = p->next) {
|
382
|
+
rb_ary_push(ary, #{Rubber.explicit_cast('(('+ct+') p->data )', ct, 'VALUE')});
|
383
|
+
}
|
384
|
+
return ary;
|
385
|
+
}
|
386
|
+
EOADD
|
387
|
+
$custom_maps[rule] ||={}
|
388
|
+
$custom_maps[rule]['VALUE'] = function+"(%%)"
|
389
|
+
end
|
364
390
|
else
|
365
391
|
returntype = 'VALUE'
|
366
392
|
end
|
@@ -380,6 +406,8 @@ def _scan(fp)
|
|
380
406
|
stack.last.functions.push(func)
|
381
407
|
puts "def "+ func.fullname
|
382
408
|
stack.push(func)
|
409
|
+
func.source_line = current_line
|
410
|
+
func.source_file = current_file
|
383
411
|
state.in_func = true
|
384
412
|
|
385
413
|
elsif state.in_func == false and @str.skip(/(string|integer|float|double|int)(?= )/x) # C String as module wrapper
|
@@ -404,16 +432,61 @@ def _scan(fp)
|
|
404
432
|
klass = C_Float
|
405
433
|
end
|
406
434
|
stack.last.classes.push(klass.new(name, string, stack.last)) if klass
|
407
|
-
|
435
|
+
elsif state.in_func == false && @str.skip(/(array)(?= )/x)
|
436
|
+
type = @str[1]
|
437
|
+
@str.skip(/\s+/)
|
438
|
+
name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
|
439
|
+
@str.skip(/\s*=\s*/)
|
440
|
+
values = []
|
441
|
+
if @str.skip(/\[/)
|
442
|
+
@str.skip(/\s+/)
|
443
|
+
until @str.skip(/\s*\]/)
|
444
|
+
if @str.skip(/(".*?[^\\]")/)
|
445
|
+
values << { :string => @str[1] } # unescape escaped chars?
|
446
|
+
elsif @str.skip(/(\d[.]\d+)/)
|
447
|
+
values << { :float => @str[1] }
|
448
|
+
elsif @str.skip(/(\d+)/)
|
449
|
+
values << { :int => @str[1] }
|
450
|
+
elsif @str.skip(/NULL/)
|
451
|
+
values << { :nil => true }
|
452
|
+
elsif @str.skip(/(TRUE|FALSE)/)
|
453
|
+
values << { :bool => @str[1] == 'TRUE' }
|
454
|
+
elsif @str.skip(/([A-Za-z0-9_]+)/)
|
455
|
+
values << { :int => @str[1] } # Assume a constant
|
456
|
+
else
|
457
|
+
syntax_error "Unrecognised array value"
|
458
|
+
end
|
459
|
+
@str.skip(/\s*,\s*/)
|
460
|
+
end
|
461
|
+
stack.last.classes.push(C_Array.new(name, values, stack.last))
|
462
|
+
p [ :create_array, values ]
|
463
|
+
else
|
464
|
+
syntax_error "Arrays should be in the form: [value1, value2, ... valueN]"
|
465
|
+
end
|
408
466
|
elsif txt = @str.get_byte # Spare chars
|
409
467
|
if state.in_func
|
410
468
|
func.text += txt
|
411
469
|
else
|
412
|
-
|
470
|
+
syntax_error "Invalid character #{txt}"
|
413
471
|
end
|
414
472
|
end
|
415
473
|
end
|
416
474
|
end
|
475
|
+
def current_line
|
476
|
+
count = 0
|
477
|
+
@string[0..@str.pos].each_byte { |b| count += 1 if b == 10 }
|
478
|
+
count
|
479
|
+
end
|
480
|
+
def syntax_error(message)
|
481
|
+
STDERR.puts "Syntax Error: #{message} at line #{current_line}\n"
|
482
|
+
if @str.rest.size > 255
|
483
|
+
STDERR.puts @str.rest[0..255]+"..."
|
484
|
+
else
|
485
|
+
STDERR.puts @str.rest
|
486
|
+
end
|
487
|
+
exit 1
|
488
|
+
end
|
489
|
+
|
417
490
|
end
|
418
491
|
|
419
492
|
end # m Rubber
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 7
|
9
|
+
version: 0.0.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Geoff Youngs
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-07 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -29,6 +29,7 @@ extra_rdoc_files:
|
|
29
29
|
files:
|
30
30
|
- bin/rubber-generate
|
31
31
|
- lib/rubber/autord.rb
|
32
|
+
- lib/rubber/codegen/array.rb
|
32
33
|
- lib/rubber/codegen/class.rb
|
33
34
|
- lib/rubber/codegen/enum.rb
|
34
35
|
- lib/rubber/codegen/flags.rb
|