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 CHANGED
@@ -1,6 +1,6 @@
1
1
  h1. Rubber Generate
2
2
 
3
- h2. v0.0.5
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
- warn("<TYPE:VALUE> is deprecated - please use <{FROM_TYPE>TO_TYPE:VALUE}> instead.")
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::native_type?(returntype)
191
- io << Rubber::explicit_cast(retval, returntype, 'VALUE')
193
+ unless Rubber.native_type?(returntype)
194
+ io << Rubber.explicit_cast(retval, returntype, 'VALUE')
192
195
  else
193
196
  io << retval
194
197
  end
@@ -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
- io.puts '#include "rbglib.h"' if scanner.options.glib?
21
+ if scanner.options.glib?
22
+ io.puts <<-EOGLIB
23
+ #include "rbglib.h"
24
+
25
+ EOGLIB
26
+
22
27
 
23
- if scanner.options.glib? and scanner.options.gtk?
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'
@@ -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
- raise "Unknown option #{@str[1]}"
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
- raise "%flags directive cannot be used here (#{stack.last.class} doesn't respond to #{flag})"
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
- raise "%feature '#{@str[1]}' directive cannot be used here (#{stack.last.class} doesn't support it)"
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
- raise "#{name} is not a GObject or GInterface..."
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
- raise "Invalid stack entry #{last.class}"
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
- raise "Invalid #{where}_func definition: #{@str.peek(200).inspect}"
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
- puts '"' << txt << '"'
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
- - 6
9
- version: 0.0.6
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-05 00:00:00 +01:00
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