rubber-generate 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|