rubber-generate 0.0.21 → 0.0.22

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.
@@ -5,11 +5,18 @@ def generate_c_source(scanner, io)
5
5
  if scanner.options.gnu
6
6
  io.puts "#define _GNU_SOURCE 1"
7
7
  end
8
- io.puts "/* Includes */"
9
- io.puts "#include <ruby.h>"
10
- io.puts "#include <stdlib.h>"
11
- io.puts "#include <stdio.h>"
12
- io.puts "#include <string.h>"
8
+ io.puts <<-EOX
9
+ /* Includes */
10
+ #include <ruby.h>
11
+ #include <stdlib.h>
12
+ #include <stdio.h>
13
+ #include <string.h>
14
+ #if defined GCC
15
+ #define OPTIONAL_ATTR __attribute__((unused))
16
+ #else
17
+ #define OPTIONAL_ATTR
18
+ #endif
19
+ EOX
13
20
  if scanner.incs
14
21
  scanner.incs.each { |i| io.puts "#include #{i.inspect}"}
15
22
  end
@@ -38,16 +38,16 @@ class C_Class < C_Module
38
38
  io.puts " c#{name} = #{cname};"
39
39
  else
40
40
  if parent and parent.cname
41
- io.puts " #{cname} = rb_define_class_under(#{parent.cname}, #{name.inspect}, #{Rubber::find_class(superclass) || 'rb_cObject'});"
41
+ io.puts " #{cname} = rb_define_class_under(#{parent.cname}, #{name.inspect}, #{Rubber.find_class(superclass) || 'rb_cObject'});"
42
42
  else
43
- io.puts " #{cname} = rb_define_class(#{name.inspect}, #{Rubber::find_class(superclass) || 'rb_cObject'});"
43
+ io.puts " #{cname} = rb_define_class(#{name.inspect}, #{Rubber.find_class(superclass) || 'rb_cObject'});"
44
44
  end
45
45
  end
46
46
  register_children(io)
47
47
  end
48
48
  include RegisterChildren
49
49
  def default_cname
50
- "c"+name
50
+ "c#{name}"
51
51
  end
52
52
  def check_wrap_ok(io, fn, where)
53
53
  case where
@@ -57,8 +57,8 @@ class C_Function
57
57
  if @multi
58
58
  io.write "int __p_argc, VALUE *__p_argv, VALUE self"
59
59
  else
60
- io.write "VALUE self"
61
- io.write(args.reject {|i| i.block }.collect { |i| ', VALUE ' + i.cname }.join(''))
60
+ io.write "VALUE self OPTIONAL_ATTR "
61
+ io.write(args.reject {|i| i.block }.collect { |i| ', VALUE ' + i.cname + " OPTIONAL_ATTR" }.join(''))
62
62
  #io.write ", " if args.size > 1 or (args.size > 0 and not @block)
63
63
  end
64
64
  io.write ")"
@@ -131,7 +131,7 @@ class C_Function
131
131
  if arg.auto_convert?
132
132
  io.puts(" __orig_#{arg.name} = #{arg.name} = #{Rubber::explicit_cast(arg.cname, 'VALUE', arg.ctype)};")
133
133
  elsif arg.block
134
- io.puts(" VALUE #{arg.name} = #{arg.init_value()};")
134
+ io.puts(" VALUE #{arg.name} OPTIONAL_ATTR = #{arg.init_value()};")
135
135
  else
136
136
  arg.check_type(io) if arg.rtype
137
137
  end
@@ -218,7 +218,7 @@ class C_Function
218
218
  io = oio
219
219
 
220
220
 
221
- io.puts " VALUE __p_retval = #{default()};" if returned
221
+ io.puts " VALUE __p_retval OPTIONAL_ATTR = #{default()};" if returned
222
222
  io.puts setupvars
223
223
  io.puts "\n do {" if @vars and not @vars.empty?
224
224
  io.puts code
@@ -1,516 +1,561 @@
1
1
  require 'rubber/struct'
2
2
  require 'rubber/version'
3
3
  module Rubber
4
- class ScanState
5
- define_members(:in_code, :in_class, :in_func, :braces)
6
- end
7
-
8
- class Options
9
- attr_accessor :gtk, :glib, :gnu
10
- alias :gtk? :gtk
11
- alias :glib? :glib
12
- def []=(name,val)
13
- instance_variable_set("@"+name,val)
14
- end
15
- def [](name)
16
- instance_variable_get("@"+name)
17
- end
18
- end
4
+ class ScanState
5
+ define_members(:in_code, :in_class, :in_func, :braces)
6
+ end
19
7
 
20
- class CRScanner
21
- attr_reader :file, :functions, :nested_functions, :classes, :calls, :allocs, :stack, :ext, :pkgs, :incs, :doc, :raw, :libs, :defs, :pre_init_code, :post_init_code, :inc_dirs, :lib_dirs
22
- attr_reader :options
23
- def initialize(file)
24
- @file = file
25
- @ext = File::basename(file).gsub(/\..*$/, '')
26
- @nested_functions = {}
27
- @functions = {}
28
- @modules = {}
29
- @classes = {}
30
- @calls = []
31
- @allocs = []
32
- @options = Options.new
33
- # Default settings
34
- @options.glib= true
35
- @options.gtk= true
36
- @options.gnu= false
37
- @current_file = file
38
- end
39
- attr_reader :current_file
40
- def scan_args()
41
- args = []
42
- return args unless @str.peep(1) == '('
43
- brackets = 1
44
- arg = ''
45
- @str.get_byte # Get first bracket
46
- until @str.peep(1) == ')' and brackets == 1
47
- brackets += 1 if @str.peep(1) == '('
48
- brackets -= 1 if @str.peep(1) == ')'
49
- if brackets == 1 and @str.peep(1) == ','
50
- @str.pos = @str.pos + 1
51
- arg.strip!
52
- args.push arg unless arg.empty?
53
- arg = ''
54
- else
55
- arg += @str.get_byte
8
+ class Options
9
+ attr_accessor :gtk, :glib, :gnu
10
+ alias :gtk? :gtk
11
+ alias :glib? :glib
12
+ def []=(name,val)
13
+ instance_variable_set("@"+name,val)
14
+ end
15
+ def [](name)
16
+ instance_variable_get("@"+name)
56
17
  end
57
18
  end
58
- @str.get_byte # Get last bracket
59
- arg.strip!
60
- args.push arg unless arg.empty?
61
- args
62
- end
63
- def stack()
64
- #p @stack
65
- @stack
66
- end
67
- attr_accessor :state
68
- def scan(fp)
69
- _scan(fp)
70
- rescue Exception
71
- off,ind = 0,0
72
- for line in @lines
73
- off += line.size
74
- break if off > @str.pos
75
- ind += 1
19
+
20
+ class TokenStringScanner < StringScanner
21
+ def skip(*args)
22
+ x = super
23
+ #p [x, *args] if x
24
+ x
76
25
  end
77
- #p @state, @str, ind
78
- raise
79
- end
80
- def _scan(fp)
81
- @lines = IO.readlines(@file)
82
- @str = StringScanner.new(@string = @lines.join)
83
- tokens = []
84
- @state = ScanState.new(0,0,false,0)
85
- @stack = [C_RootModule.new]
86
- func = nil
87
- until @str.empty?
88
- ######## Doc
89
- if @str.skip(/=begin([ \t][^\n]*)?\n/) # skip =begin/=end blocks
90
- lines = @str.scan_until(/\n=end([ \t].*)?/).split("\n")
91
- lines.pop
92
- if state.in_func
93
- (func.doc ||= "") << lines.join("\n")
94
- elsif @class and not @class.kind_of?(C_RootModule)
95
- (@class.doc ||= "") << lines.join("\n")
96
- else
97
- (@doc ||= "") << lines.join("\n")
98
- end
99
-
100
- ######## Config
101
-
102
- elsif @str.skip(/%\{/) # Scan raw
103
- raw = @str.scan_until(/%\}/)
104
- raw[-2..-1] = ""
105
- (@raw ||= "") << raw
106
- elsif @str.skip(/%pre_init\{/) # Scan raw
107
- raw = @str.scan_until(/%\}/)
108
- raw[-2..-1] = ""
109
- (@pre_init_code ||= "") << raw
110
- elsif @str.skip(/%post_init\{/) # Scan raw
111
- raw = @str.scan_until(/%\}/)
26
+ def scan(*args)
27
+ x = super
28
+ #p [x, *args] if x
29
+ x
30
+ end
31
+ def scan_number
32
+ scan(/(0x[0-9A-Fa-f]+|[0-9]+[.][0-9]+(e[0-9]+)?|[0-9]+)/)
33
+ end
34
+ def scan_lit_string
35
+ scan(/(".*?[^\\]")/)
36
+ end
37
+ def scan_float
38
+ scan(/(\d[.]\d+)/)
39
+ end
40
+ def scan_lit_integer
41
+ scan(/([0-9]+)/)
42
+ end
43
+ def scan_constant
44
+ scan(/([A-Z][a-z_0-9A-Z]*)/)
45
+ end
46
+ def scan_upcase_constant
47
+ scan(/([A-Z][0-9A-Z_]*)/)
48
+ end
49
+ def scan_literal
50
+ scan(/([A-Za-z0-9_]+)/)
51
+ end
52
+ def skip_ws
53
+ skip(/\s+/)
54
+ end
55
+ def scan_nil
56
+ scan(/NULL|nil/i)
57
+ end
58
+ def scan_lit_bool
59
+ scan(/TRUE|FALSE/i)
60
+ end
61
+ def scan_pc_block
62
+ raw = scan_until(/%\}/)
112
63
  raw[-2..-1] = ""
113
- (@post_init_code ||= "") << raw
114
- elsif @str.skip(/\s+/) # skip
115
- func.text += " " if state.in_func
116
- elsif @str.skip(/%name */) # Extension name
117
- @ext = @str.scan(/[a-zA-Z0-9]+/)
118
- elsif @str.skip(/%min-version */)
119
- @version = @str.scan(/([0-9]+)\.([0-9]+)\.([0-9]+)/)
120
- version = [1,2,3].map{|i|@str[i].to_i}
121
- Rubber::VERSION.each_with_index do |ver,idx|
122
- if ver < version[idx]
123
- misc_error "This version of rubber-generate (#{Rubber::VERSION}) is too old: #{@file} requires version #{version.map{|i|i.to_s}.join('.')}", false
124
- end
125
- end
126
- elsif @str.skip(/%pkg-config\s*([-a-z.0-9+]+)/) # pkg-config library
127
- @pkgs ||= []
128
- @pkgs << @str[1]
129
- elsif @str.skip(/%include_dir\s+(.+)\n/) # Include dirs
130
- @inc_dirs ||= []
131
- @inc_dirs << @str[1].strip
132
- elsif @str.skip(/%lib_dir\s+(.+)\n/) # Library dir
133
- @lib_dirs ||= []
134
- @lib_dirs << @str[1].strip
135
- elsif @str.skip(/%include\s+(.+)\n/) # Include file
136
- @incs ||= []
137
- @incs << @str[1].strip
138
- elsif @str.skip(/%option +([a-z]+)=(yes|no)\n/) # Option
139
- case @str[1]
140
- when 'glib', 'gtk', 'gnu'
141
- @options[@str[1]] = (@str[2] == 'yes')
142
- else
143
- syntax_error "Unknown option #{@str[1]}"
144
- end
145
- elsif @str.skip(/%lib\s+(.+)\n/) # Skip single-line comment
146
- @libs ||= []
147
- @libs << @str[1].strip
148
- elsif @str.skip(/%define\s+(.+)\n/) # Skip single-line comment
149
- @defs ||= []
150
- @defs << @str[1].strip
151
- elsif @str.skip(/%equiv[a-z]*\s+([^=\n]+)=([^=\n]+)\n/) # Skip single-line comment
152
- $equivalents[@str[1].gsub(/ /,'').strip] = @str[2].gsub(/ /,'').strip
153
- elsif @str.skip(/%map\s+([^->]+?)\>([^:]+):([^@\n]+) *@? *(.*)\n/) # Skip single-line comment
154
- from, to, code = *[@str[1], @str[2], @str[3]].collect { |i| i.strip }
155
- free_code = @str[4]
156
- from.gsub!(/ /,'')
157
- to.gsub!(/ /,'')
158
- puts "Mapping #{from} -> #{to}"
159
- ($custom_maps[from] ||= {})[to] = code
160
- $custom_frees[to] = free_code
161
- elsif state.in_class > 0 and state.in_func == false and @str.skip(/%flag\s+([a-z0-9_A-Z]+)/)
162
- flag = ("flag_"+@str[1]+"=").intern
163
- if stack.last.respond_to?(flag)
164
- stack.last.__send__(flag, true)
165
- else
166
- syntax_error "%flags directive cannot be used here (#{stack.last.class} doesn't respond to #{flag})"
167
- end
168
- elsif state.in_class > 0 and state.in_func == false and @str.skip(/%feature\s+([a-z0-9_A-Z]+)/)
169
- flag = ("feature_"+@str[1]).intern
170
- @str.skip(/\s+/)
171
- args = []
172
- if @str.skip(/\(/)
173
- args = @str.scan_until(/\)/)[0..-2].split(/, */).map { |i| i.strip }
64
+ raw
65
+ end
66
+ end
67
+
68
+ class CRScanner
69
+ attr_reader :file, :functions, :nested_functions, :classes, :calls, :allocs, :stack, :ext, :pkgs, :incs, :doc, :raw, :libs, :defs, :pre_init_code, :post_init_code, :inc_dirs, :lib_dirs
70
+ attr_reader :options
71
+ def initialize(file)
72
+ @file = file
73
+ @ext = File::basename(file).gsub(/\..*$/, '')
74
+ @nested_functions = {}
75
+ @functions = {}
76
+ @modules = {}
77
+ @classes = {}
78
+ @calls = []
79
+ @allocs = []
80
+ @options = Options.new
81
+ # Default settings
82
+ @options.glib= true
83
+ @options.gtk= true
84
+ @options.gnu= false
85
+ @current_file = file
86
+ end
87
+ attr_reader :current_file
88
+ def scan_args()
89
+ args = []
90
+ return args unless @str.peep(1) == '('
91
+ brackets = 1
92
+ arg = ''
93
+ @str.get_byte # Get first bracket
94
+ until @str.peep(1) == ')' and brackets == 1
95
+ brackets += 1 if @str.peep(1) == '('
96
+ brackets -= 1 if @str.peep(1) == ')'
97
+ if brackets == 1 and @str.peep(1) == ','
98
+ @str.pos = @str.pos + 1
99
+ arg.strip!
100
+ args.push arg unless arg.empty?
101
+ arg = ''
102
+ else
103
+ arg += @str.get_byte
174
104
  end
175
- @str.skip(/;/)
176
- if stack.last.respond_to?(flag)
177
- stack.last.__send__(flag, *args)
178
- else
179
- syntax_error "%feature '#{@str[1]}' directive cannot be used here (#{stack.last.class} doesn't support it)"
180
- end
181
- elsif @str.skip(/%/) # Skip single-line comment
182
- @str.skip_until(/\n/)
183
-
184
-
185
- ####### Comments
186
-
187
- elsif state.in_func == false and @str.skip(/#.*/) # Comment
188
-
189
- elsif state.in_func == false and @str.skip(/\/\/.*/) # Comment
190
-
191
- elsif state.in_func == false and @str.skip(/\/\*(.|\n)*\*\//) # Multi-line Comment
192
-
193
-
194
-
195
- ####### Code
196
-
197
- elsif txt = @str.scan(/['"]/) #' Skip quoted string
198
- txt += @str.scan_until(/(^|[^\\])#{txt}/) # Skip until unescaped quote
199
- func.text += txt if state.in_func
200
- elsif state.in_func == false and @str.skip(/module(?= )/x) # Module defn
201
- @str.skip(/\s+/)
202
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
203
- @classes[name] = C_Module.new(name, [], [], [])
204
- @classes[name].parent = stack.last
205
- stack.last.classes.push(@classes[name])
206
- stack.push(@class = @classes[name])
207
- puts "module "+ @class.fullname + "-#{@class.name}"
208
- state.in_class += 1
209
- elsif state.in_func == false and @str.skip(/class(?= )/x) # Class defn
210
- @str.skip(/\s+/)
211
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
212
- superclass = nil
213
- @str.skip(/\s*/)
214
- if @str.skip(/</)
215
- @str.skip(/\s*/)
216
- superclass = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
217
- end
218
- @classes[name] = C_Class.new(name, superclass, [], [], [])
219
- @classes[name].parent = stack.last
220
- stack.last.classes.push(@classes[name])
221
- stack.push(@class = @classes[name])
222
- puts "class "+ @class.fullname
223
- state.in_class += 1
224
-
225
- elsif state.in_func == false and @str.skip(/struct(?= )/x) # Ruby Struct
226
- @str.skip(/\s+/)
227
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
228
- @str.skip(/\s+/)
229
- if @str.skip(/\(/)
230
- args = @str.scan_until(/\)/)[0..-2].split(/, */).collect { |i| i.strip }
231
- end
232
- # NB. struct Name(arg0...argN)
233
- if @str.skip(/\s*;/)
234
- stack.last.classes.push(C_Struct.new(name, args, stack.last))
235
- else
236
- @classes[name] = C_Struct.new(name, args, stack.last)
237
- stack.last.classes.push(@classes[name])
238
- stack.push(@class = @classes[name])
239
- puts "struct "+ @class.fullname
240
- state.in_class += 1
241
- end
242
- elsif state.in_func == false and @str.skip(/gcpool(?= )/x) # GC Pool
243
- @str.skip(/\s+/)
244
- name = @str.scan(/[a-z_0-9A-Z]*/)
245
- puts "GCPool #{name}"
246
- stack.first.classes.push(C_GCRefPool.new(name))
247
-
248
- elsif state.in_func == false and @str.skip(/(enum|flags)(?= )/x) # C Enum as module wrapper
249
- what = @str[1]
250
- @str.skip(/\s+/)
251
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
252
- @str.skip(/\s+/)
253
- if @str.skip(/\(/)
254
- args = @str.scan_until(/\)/)[0..-2].split(/, */).collect { |i| i.strip }
255
105
  end
256
- if what == "flags"
257
- stack.last.classes.push(C_Flags.new(name, args, stack.last))
258
- else
259
- stack.last.classes.push(C_Enum.new(name, args, stack.last))
106
+ @str.get_byte # Get last bracket
107
+ arg.strip!
108
+ args.push arg unless arg.empty?
109
+ args
110
+ end
111
+ def stack()
112
+ #p @stack
113
+ @stack
114
+ end
115
+ attr_accessor :state
116
+ def scan(fp)
117
+ _scan(fp)
118
+ rescue Exception
119
+ off,ind = 0,0
120
+ for line in @lines
121
+ off += line.size
122
+ break if off > @str.pos
123
+ ind += 1
260
124
  end
125
+ #p @state, @str, ind
126
+ raise
127
+ end
128
+ def _scan(fp)
129
+ @lines = IO.readlines(@file)
130
+ @str = TokenStringScanner.new(@string = @lines.join)
131
+ tokens = []
132
+ @state = ScanState.new(0,0,false,0)
133
+ @stack = [C_RootModule.new]
134
+ func = nil
135
+ until @str.empty?
136
+ ######## Doc
137
+ if @str.skip(/=begin([ \t][^\n]*)?\n/) # skip =begin/=end blocks
138
+ lines = @str.scan_until(/\n=end([ \t].*)?/).split("\n")
139
+ lines.pop
140
+ if state.in_func
141
+ (func.doc ||= "") << lines.join("\n")
142
+ elsif @class and not @class.kind_of?(C_RootModule)
143
+ (@class.doc ||= "") << lines.join("\n")
144
+ else
145
+ (@doc ||= "") << lines.join("\n")
146
+ end
261
147
 
262
- elsif state.in_func == false and @str.skip(/(genum|gflags)(?= )/x) # C GEnum as module wrapper
263
- what = @str[1]
264
- @str.skip(/\s+/)
265
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
266
- @str.skip(/\s+/)
267
- g_type = @str.scan(/[A-Z][_0-9A-Z]*/)
268
- @str.skip(/\s+/)
269
- prefix = @str.scan(/(prefix=)?[A-Z][_0-9A-Z]*/i).to_s.sub(/^prefix=/i,'')
270
- @str.skip(/\s*/)
271
- define_on_self = @str.scan(/define_on_self/)
272
- @str.skip(/\s*;/)
273
- if what == "gflags"
274
- obj = C_GFlags.new(name, g_type, prefix, stack.last)
275
- else
276
- obj = C_GEnum.new(name, g_type, prefix, stack.last)
277
- obj.define_on_self = !! define_on_self;
278
- end
279
- stack.last.classes.push(obj)
280
- elsif state.in_func == false and @str.skip(/(gobject|ginterface|gboxed)(?= )/x) # Class defn
281
- type=@str[1]
282
- @str.skip(/\s+/)
283
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
284
- superclass = nil
285
- @str.skip(/\s*/)
286
- if @str.skip(/\</)
287
- @str.skip(/\s*/)
288
- gtype = @str.scan(/[A-Z_]+/)
289
- end
290
- @str.skip(/\s*/)
291
- if @str.skip(/:/)
292
- @str.skip(/\s*/)
293
- gparent_class = @str.scan(/[A-Z_:a-z0-9]+/)
294
- end
295
- case type
296
- when "gobject"
297
- @classes[name] = C_GObject.new(name, gtype, [], [], [])
298
- when "ginterface"
299
- @classes[name] = C_GInterface.new(name, gtype, [], [], [])
300
- when "gboxed"
301
- @classes[name] = C_GBoxed.new(name, gtype, [], [], [])
302
- else
303
- syntax_error "#{name} is not a GObject or GInterface..."
304
- end
305
- @classes[name].gparent_class = gparent_class
306
- @classes[name].parent = stack.last
307
- stack.last.classes.push(@classes[name])
308
- stack.push(@class = @classes[name])
309
- puts "class "+ @class.fullname
310
- state.in_class += 1
311
- elsif @str.scan(/@type\s+([A-Za-z_0-9]+)/)
312
- type = @str[1]
313
- prev = stack.last
314
- if prev.is_a?(C_GObject)
315
- # p c_type, self
316
- puts "Converting #{type}* to & from VALUE"
317
- prev.c_type_name = type
318
- ($custom_maps[type+'*'] ||= {})["VALUE"] = "GOBJ2RVAL(%%)"
319
- ($custom_maps["VALUE"] ||= {})[type+'*'] = "RVAL2GOBJ(%%)"
320
- elsif prev.is_a?(C_GBoxed)
321
- prev.c_type_name = type
148
+ ######## Config
322
149
 
323
- ($custom_maps[type+'*'] ||= {})["VALUE"] = "BOXED2RVAL(%%, #{prev.superclass})"
324
- ($custom_maps["VALUE"] ||= {})[type+'*'] = "RVAL2BOXED(%%, #{prev.superclass})"
325
- else
326
- # Invalid type directive
327
- end
328
- elsif @str.skip(/end(?=\s)/x)
329
- last = stack.pop
330
- puts "#{last.class} - #{last.name}"
331
- case last
332
- when C_Module, C_Class, C_Enum, C_Flags, C_Struct, C_GObject, C_GInterface, C_GBoxed
333
- state.in_class -= 1
334
- @class = stack.last
335
- when C_Function
336
- state.in_func = false
337
- else
338
- STDERR.puts "Remaining code: #{@str.rest}"
339
- STDERR.puts "Defined Classes: #{@classes.keys.join(', ')}"
340
- p stack
341
- syntax_error "Invalid stack entry #{last.class}"
342
- end
343
- elsif @str.skip(/alias\s+:([A-Za-z0-9_]*[\[\]]{0,2}[?!=]?)\s+:([A-Za-z0-9_]*[\[\]]{0,2}[?!=]?)/) # Alias
344
- @class.add_alias(@str[1], @str[2]) if @class.respond_to?(:add_alias)
345
- elsif @str.skip(/(pre|post)_func\s+do/)
346
- where = @str[1]
347
- str = @str.scan_until(/\bend/)#[0,-4]
348
- unless str
349
- syntax_error "Invalid #{where}_func definition"
350
- end
351
- str = str[0..-4].strip
352
- except = only = nil
353
- @str.skip(/\s*/)
354
- if @str.skip(/,\s*:(only|except)\s*=\>\s*(\[[^\]]+\])/)
355
- if @str[1] == 'only'
356
- only = eval(@str[2]).map{|i|i.to_s}
357
- else
358
- except = eval(@str[2]).map{|i|i.to_s}
359
- end
360
- end
361
- if where == 'pre'
362
- @class.pre_func = str
363
- @class.pre_only = only if only
364
- @class.pre_except = except if except
365
- else
366
- @class.post_func = str
367
- @class.post_only = only if only
368
- @class.post_except = except if except
369
- end
370
- elsif @str.skip(/pre_func\s+(.*)/) # Pre func code
371
- @class.pre_func= @str[1] if @class.respond_to?(:pre_func)
372
- elsif @str.skip(/post_func\s+(.*)/) # Post func code
373
- @class.post_func= @str[1] if @class.respond_to?(:post_func)
374
- elsif @str.skip(/def(?=\s+)/x) # Function defn
375
- @str.skip(/\s+/)
376
- if @str.scan(/([a-zA-Z_* 0-9]+):/)
377
- returntype = @str[1]
378
- elsif @str.skip(/(GS?List)[{]([^}]+)[}]:/)
379
- container = @str[1]
380
- ct = @str[2]
381
- cn = ct.gsub(/\s+/,'').gsub(/[*]/,'__p')
382
- rule = container+'{'+ct+'}'
383
- returntype = rule
384
-
385
- #syntax_error "Auto converting (#{rule}) GSList of #{ct} is not yet supported"
386
- # sane
387
- unless $custom_maps[rule] && $custom_maps[rule]['VALUE']
388
- function = "rubber_#{container}_of_#{cn}_to_array"
389
- @raw = @raw.to_s + <<-EOADD
150
+ elsif @str.skip(/%\{/) # Scan raw
151
+ (@raw ||= "") << @str.scan_pc_block
152
+ elsif @str.skip(/%pre_init\{/) # Scan raw
153
+ (@pre_init_code ||= "") << @str.scan_pc_block
154
+ elsif @str.skip(/%post_init\{/) # Scan raw
155
+ (@post_init_code ||= "") << @str.scan_pc_block
156
+ elsif @str.skip_ws # skip
157
+ func.text += " " if state.in_func
158
+ elsif @str.skip(/%name */) # Extension name
159
+ @ext = @str.scan_literal
160
+ elsif @str.skip(/%min-version */)
161
+ @version = @str.scan(/([0-9]+)\.([0-9]+)\.([0-9]+)/)
162
+ version = [1,2,3].map{|i|@str[i].to_i}
163
+ Rubber::VERSION.each_with_index do |ver,idx|
164
+ if ver < version[idx]
165
+ misc_error "This version of rubber-generate (#{Rubber::VERSION}) is too old: #{@file} requires version #{version.map{|i|i.to_s}.join('.')}", false
166
+ end
167
+ end
168
+ elsif @str.skip(/%pkg-config\s*([-a-z.0-9+]+)/) # pkg-config library
169
+ @pkgs ||= []
170
+ @pkgs << @str[1]
171
+ elsif @str.skip(/%include_dir\s+(.+)\n/) # Include dirs
172
+ @inc_dirs ||= []
173
+ @inc_dirs << @str[1].strip
174
+ elsif @str.skip(/%lib_dir\s+(.+)\n/) # Library dir
175
+ @lib_dirs ||= []
176
+ @lib_dirs << @str[1].strip
177
+ elsif @str.skip(/%include\s+(.+)\n/) # Include file
178
+ @incs ||= []
179
+ @incs << @str[1].strip
180
+ elsif @str.skip(/%option +([a-z]+)=(yes|no)\n/) # Option
181
+ case @str[1]
182
+ when 'glib', 'gtk', 'gnu'
183
+ @options[@str[1]] = (@str[2] == 'yes')
184
+ else
185
+ syntax_error "Unknown option #{@str[1]}"
186
+ end
187
+ elsif @str.skip(/%lib\s+(.+)\n/) # Skip single-line comment
188
+ @libs ||= []
189
+ @libs << @str[1].strip
190
+ elsif @str.skip(/%define\s+(.+)\n/) # Skip single-line comment
191
+ @defs ||= []
192
+ @defs << @str[1].strip
193
+ elsif @str.skip(/%equiv[a-z]*\s+([^=\n]+)=([^=\n]+)\n/) # Skip single-line comment
194
+ $equivalents[@str[1].gsub(/ /,'').strip] = @str[2].gsub(/ /,'').strip
195
+ elsif @str.skip(/%map\s+([^->]+?)\>([^:]+):([^@\n]+) *@? *(.*)\n/) # Skip single-line comment
196
+ from, to, code = *[@str[1], @str[2], @str[3]].collect { |i| i.strip }
197
+ free_code = @str[4]
198
+ from.gsub!(/ /,'')
199
+ to.gsub!(/ /,'')
200
+ puts "Mapping #{from} -> #{to}"
201
+ ($custom_maps[from] ||= {})[to] = code
202
+ $custom_frees[to] = free_code
203
+ elsif state.in_class > 0 and state.in_func == false and @str.skip(/%flag\s+([a-z0-9_A-Z]+)/)
204
+ flag = ("flag_"+@str[1]+"=").intern
205
+ if stack.last.respond_to?(flag)
206
+ stack.last.__send__(flag, true)
207
+ else
208
+ syntax_error "%flags directive cannot be used here (#{stack.last.class} doesn't respond to #{flag})"
209
+ end
210
+ elsif state.in_class > 0 and state.in_func == false and @str.skip(/%feature\s+([a-z0-9_A-Z]+)/)
211
+ flag = ("feature_"+@str[1]).intern
212
+ @str.skip(/\s+/)
213
+ args = []
214
+ if @str.skip(/\(/)
215
+ args = @str.scan_until(/\)/)[0..-2].split(/, */).map { |i| i.strip }
216
+ end
217
+ @str.skip(/;/)
218
+ if stack.last.respond_to?(flag)
219
+ stack.last.__send__(flag, *args)
220
+ else
221
+ syntax_error "%feature '#{@str[1]}' directive cannot be used here (#{stack.last.class} doesn't support it)"
222
+ end
223
+ elsif @str.skip(/%/) # Skip single-line comment
224
+ @str.skip_until(/\n/)
225
+
226
+
227
+ ####### Comments
228
+
229
+ elsif state.in_func == false and @str.skip(/#.*/) # Comment
230
+
231
+ elsif state.in_func == false and @str.skip(/\/\/.*/) # Comment
232
+
233
+ elsif state.in_func == false and @str.skip(/\/\*(.|\n)*\*\//) # Multi-line Comment
234
+
235
+
236
+ ####### Code
237
+
238
+ elsif txt = @str.scan(/['"]/) #' Skip quoted string
239
+ txt += @str.scan_until(/(^|[^\\])#{txt}/) # Skip until unescaped quote
240
+ func.text += txt if state.in_func
241
+ elsif state.in_func == false and @str.skip(/module(?= )/x) # Module defn
242
+ @str.skip(/\s+/)
243
+ name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
244
+ @classes[name] = C_Module.new(name, [], [], [])
245
+ @classes[name].parent = stack.last
246
+ stack.last.classes.push(@classes[name])
247
+ stack.push(@class = @classes[name])
248
+ puts "module "+ @class.fullname + "-#{@class.name}"
249
+ state.in_class += 1
250
+ elsif state.in_func == false and @str.skip(/class(?= )/x) # Class defn
251
+ @str.skip_ws
252
+ name = @str.scan_constant
253
+ superclass = nil
254
+ @str.skip(/\s*/)
255
+ if @str.skip(/</)
256
+ @str.skip_ws
257
+ superclass = @str.scan_constant
258
+ end
259
+ @classes[name] = C_Class.new(name, superclass, [], [], [])
260
+ @classes[name].parent = stack.last
261
+ stack.last.classes.push(@classes[name])
262
+ stack.push(@class = @classes[name])
263
+ puts "class "+ @class.fullname
264
+ state.in_class += 1
265
+
266
+ elsif state.in_func == false and @str.skip(/struct(?= )/x) # Ruby Struct
267
+ @str.skip_ws
268
+ name = @str.scan_constant
269
+ @str.skip_ws
270
+ if @str.skip(/\(/)
271
+ args = @str.scan_until(/\)/)[0..-2].split(/, */).collect { |i| i.strip }
272
+ end
273
+ # NB. struct Name(arg0...argN)
274
+ if @str.skip(/\s*;/)
275
+ stack.last.classes.push(C_Struct.new(name, args, stack.last))
276
+ else
277
+ @classes[name] = C_Struct.new(name, args, stack.last)
278
+ stack.last.classes.push(@classes[name])
279
+ stack.push(@class = @classes[name])
280
+ puts "struct "+ @class.fullname
281
+ state.in_class += 1
282
+ end
283
+ elsif state.in_func == false and @str.skip(/gcpool(?= )/x) # GC Pool
284
+ @str.skip_ws
285
+ name = @str.scan_literal
286
+ puts "GCPool #{name}"
287
+ stack.first.classes.push(C_GCRefPool.new(name))
288
+
289
+ elsif state.in_func == false and @str.skip(/(enum|flags)(?= )/x) # C Enum as module wrapper
290
+ what = @str[1]
291
+ @str.skip_ws
292
+ name = @str.scan_constant
293
+ @str.skip_ws
294
+ if @str.skip(/\(/)
295
+ args = @str.scan_until(/\)/)[0..-2].split(/, */).collect { |i| i.strip }
296
+ end
297
+ if what == "flags"
298
+ stack.last.classes.push(C_Flags.new(name, args, stack.last))
299
+ else
300
+ stack.last.classes.push(C_Enum.new(name, args, stack.last))
301
+ end
302
+
303
+ elsif state.in_func == false and @str.skip(/(genum|gflags)(?= )/x) # C GEnum as module wrapper
304
+ what = @str[1]
305
+ @str.skip_ws
306
+ name = @str.scan_constant
307
+ @str.skip_ws
308
+ g_type = @str.scan_upcase_constant
309
+ @str.skip_ws
310
+ prefix = @str.scan(/(prefix=)?[A-Z][_0-9A-Z]*/i).to_s.sub(/^prefix=/i,'')
311
+ @str.skip_ws
312
+ define_on_self = @str.scan(/define_on_self/)
313
+ @str.skip(/\s*;/)
314
+ if what == "gflags"
315
+ obj = C_GFlags.new(name, g_type, prefix, stack.last)
316
+ else
317
+ obj = C_GEnum.new(name, g_type, prefix, stack.last)
318
+ obj.define_on_self = !! define_on_self;
319
+ end
320
+ stack.last.classes.push(obj)
321
+ elsif state.in_func == false and @str.skip(/(gobject|ginterface|gboxed)(?= )/x) # Class defn
322
+ type=@str[1]
323
+ @str.skip_ws
324
+ name = @str.scan_constant
325
+ superclass = nil
326
+ @str.skip_ws
327
+ if @str.skip(/\</)
328
+ @str.skip_ws
329
+ gtype = @str.scan_upcase_constant
330
+ end
331
+ @str.skip_ws
332
+ if @str.skip(/:/)
333
+ @str.skip_ws
334
+ gparent_class = @str.scan(/[A-Z_:a-z0-9]+/)
335
+ end
336
+ case type
337
+ when "gobject"
338
+ @classes[name] = C_GObject.new(name, gtype, [], [], [])
339
+ when "ginterface"
340
+ @classes[name] = C_GInterface.new(name, gtype, [], [], [])
341
+ when "gboxed"
342
+ @classes[name] = C_GBoxed.new(name, gtype, [], [], [])
343
+ else
344
+ syntax_error "#{name} is not a GObject or GInterface..."
345
+ end
346
+ @classes[name].gparent_class = gparent_class
347
+ @classes[name].parent = stack.last
348
+ stack.last.classes.push(@classes[name])
349
+ stack.push(@class = @classes[name])
350
+ puts "class "+ @class.fullname
351
+ state.in_class += 1
352
+ elsif @str.scan(/@type\s+/)
353
+ type = @str.scan_literal
354
+ prev = stack.last
355
+ if prev.is_a?(C_GObject)
356
+ # p c_type, self
357
+ puts "Converting #{type}* to & from VALUE"
358
+ prev.c_type_name = type
359
+ ($custom_maps[type+'*'] ||= {})["VALUE"] = "GOBJ2RVAL(%%)"
360
+ ($custom_maps["VALUE"] ||= {})[type+'*'] = "RVAL2GOBJ(%%)"
361
+ elsif prev.is_a?(C_GBoxed)
362
+ prev.c_type_name = type
363
+
364
+ ($custom_maps[type+'*'] ||= {})["VALUE"] = "BOXED2RVAL(%%, #{prev.superclass})"
365
+ ($custom_maps["VALUE"] ||= {})[type+'*'] = "RVAL2BOXED(%%, #{prev.superclass})"
366
+ else
367
+ # Invalid type directive
368
+ end
369
+ elsif @str.skip(/end(?=\s)/x)
370
+ last = stack.pop
371
+ puts "#{last.class} - #{last.name}"
372
+ case last
373
+ when C_Module, C_Class, C_Enum, C_Flags, C_Struct, C_GObject, C_GInterface, C_GBoxed
374
+ state.in_class -= 1
375
+ @class = stack.last
376
+ when C_Function
377
+ state.in_func = false
378
+ else
379
+ STDERR.puts "Remaining code: #{@str.rest}"
380
+ STDERR.puts "Defined Classes: #{@classes.keys.join(', ')}"
381
+ p stack
382
+ syntax_error "Invalid stack entry #{last.class}"
383
+ end
384
+ elsif @str.skip(/alias\s+:([A-Za-z0-9_]*[\[\]]{0,2}[?!=]?)\s+:([A-Za-z0-9_]*[\[\]]{0,2}[?!=]?)/) # Alias
385
+ @class.add_alias(@str[1], @str[2]) if @class.respond_to?(:add_alias)
386
+ elsif @str.skip(/(pre|post)_func\s+do/)
387
+ where = @str[1]
388
+ str = @str.scan_until(/\bend/)#[0,-4]
389
+ unless str
390
+ syntax_error "Invalid #{where}_func definition"
391
+ end
392
+ str = str[0..-4].strip
393
+ except = only = nil
394
+ @str.skip(/\s*/)
395
+ if @str.skip(/,\s*:(only|except)\s*=\>\s*(\[[^\]]+\])/)
396
+ if @str[1] == 'only'
397
+ only = eval(@str[2]).map{|i|i.to_s}
398
+ else
399
+ except = eval(@str[2]).map{|i|i.to_s}
400
+ end
401
+ end
402
+ if where == 'pre'
403
+ @class.pre_func = str
404
+ @class.pre_only = only if only
405
+ @class.pre_except = except if except
406
+ else
407
+ @class.post_func = str
408
+ @class.post_only = only if only
409
+ @class.post_except = except if except
410
+ end
411
+ elsif @str.skip(/pre_func\s+(.*)/) # Pre func code
412
+ @class.pre_func= @str[1] if @class.respond_to?(:pre_func)
413
+ elsif @str.skip(/post_func\s+(.*)/) # Post func code
414
+ @class.post_func= @str[1] if @class.respond_to?(:post_func)
415
+ elsif @str.skip(/def(?=\s+)/x) # Function defn
416
+ @str.skip(/\s+/)
417
+ if @str.scan(/([a-zA-Z_* 0-9]+):/)
418
+ returntype = @str[1]
419
+ elsif @str.skip(/(GS?List)[{]([^}]+)[}]:/)
420
+ container = @str[1]
421
+ ct = @str[2]
422
+ cn = ct.gsub(/\s+/,'').gsub(/[*]/,'__p')
423
+ rule = container+'{'+ct+'}'
424
+ returntype = rule
425
+
426
+ #syntax_error "Auto converting (#{rule}) GSList of #{ct} is not yet supported"
427
+ # sane
428
+ unless $custom_maps[rule] && $custom_maps[rule]['VALUE']
429
+ function = "rubber_#{container}_of_#{cn}_to_array"
430
+ @raw = @raw.to_s + <<-EOADD
390
431
  inline VALUE #{function}(#{container} *list) {
391
- #{container} *p; volatile VALUE ary;
392
- ary = rb_ary_new();
393
- for(p = list ; p ; p = p->next) {
394
- rb_ary_push(ary, #{Rubber.explicit_cast('(('+ct+') p->data )', ct, 'VALUE')});
395
- }
396
- return ary;
432
+ #{container} *p; volatile VALUE ary;
433
+ ary = rb_ary_new();
434
+ for(p = list ; p ; p = p->next) {
435
+ rb_ary_push(ary, #{Rubber.explicit_cast('(('+ct+') p->data )', ct, 'VALUE')});
436
+ }
437
+ return ary;
397
438
  }
398
- EOADD
399
- $custom_maps[rule] ||={}
400
- $custom_maps[rule]['VALUE'] = function+"(%%)"
401
- end
402
- else
403
- returntype = 'VALUE'
404
- end
405
- prename = ''
406
- prename = @str.scan(/self\./)
407
- name = @str.scan(/[a-z_0-9A-Z.]+[?!=]?/)
408
- unless name
409
- name = @str.scan(/[-\[\]<>~=+|&]{1,3}/)
410
- end
411
- oname = name
412
- name = prename.to_s + oname
413
- #p [prename, oname, name]
414
- @str.skip(/\s*/)
415
- args = scan_args().collect { |i| C_Param.new(i) }
416
- func = @functions[name] = C_Function.new(name, args, '')
417
- func.returntype = returntype
418
- func.parent = @class
419
- stack.last.functions.push(func)
420
- puts "def "+ func.fullname
421
- stack.push(func)
422
- func.source_line = current_line
423
- func.source_file = current_file
424
- state.in_func = true
425
-
426
- elsif state.in_func == false and @str.skip(/(string|integer|float|double|int)(?= )/x) # C String as module wrapper
427
- type= @str[1]
428
- @str.skip(/\s+/)
429
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
430
- @str.skip(/\s*=\s*/)
431
- if @str.skip(/"/) #"
432
- string = '"' + @str.scan_until(/[^\\]?"/) #"
433
- elsif t = @str.scan(/([A-Z][a-z_0-9A-Z]*)/)
434
- string = @str[1]
435
- elsif type =~ /^(flo|dou|int)/ and t = @str.scan(/([0-9]+(\.[0-9]+)?(e[0-9]+)?)/)
436
- string = @str[1]
437
- end
438
- klass = nil
439
- case type
440
- when /^str/
441
- klass = C_String
442
- when /^int/
443
- klass = C_Integer
444
- when /^(flo|dou)/
445
- klass = C_Float
439
+ EOADD
440
+ $custom_maps[rule] ||={}
441
+ $custom_maps[rule]['VALUE'] = function+"(%%)"
442
+ end
443
+ else
444
+ returntype = 'VALUE'
445
+ end
446
+ prename = ''
447
+ prename = @str.scan(/self\./)
448
+ name = @str.scan(/[a-z_0-9A-Z.]+[?!=]?/)
449
+ unless name
450
+ name = @str.scan(/[-\[\]<>~=+|&]{1,3}/)
451
+ end
452
+ oname = name
453
+ name = prename.to_s + oname
454
+ #p [prename, oname, name]
455
+ @str.skip(/\s*/)
456
+ args = scan_args().collect { |i| C_Param.new(i) }
457
+ func = @functions[name] = C_Function.new(name, args, '')
458
+ func.returntype = returntype
459
+ func.parent = @class
460
+ stack.last.functions.push(func)
461
+ puts "def "+ func.fullname
462
+ stack.push(func)
463
+ func.source_line = current_line
464
+ func.source_file = current_file
465
+ state.in_func = true
466
+
467
+ elsif state.in_func == false and @str.skip(/(string|integer|float|double|int)(?= )/x) # C String as module wrapper
468
+ type= @str[1]
469
+ @str.skip_ws
470
+ name = @str.scan_constant
471
+ @str.skip(/\s*=\s*/)
472
+ if @str.skip(/"/) #"
473
+ string = '"' + @str.scan_until(/[^\\]?"/) #"
474
+ elsif t = @str.scan_constant
475
+ string = @str[1]
476
+ elsif type =~ /^(flo|dou|int)/ and t = @str.scan_number #@str.scan(/(0x)?([0-9]+(\.[0-9]+)?(e[0-9]+)?)/)
477
+ string = @str[1]
478
+ end
479
+ klass = nil
480
+ case type
481
+ when /^str/
482
+ klass = C_String
483
+ when /^int/
484
+ klass = C_Integer
485
+ when /^(flo|dou)/
486
+ klass = C_Float
487
+ end
488
+ stack.last.classes.push(klass.new(name, string, stack.last)) if klass
489
+ elsif state.in_func == false && @str.skip(/(array)(?= )/x)
490
+ type = @str[1]
491
+ @str.skip_ws
492
+ name = @str.scan_constant
493
+ @str.skip_ws
494
+ @str.skip(/=/)
495
+ @str.skip_ws
496
+ values = []
497
+ if @str.skip(/\[/)
498
+ @str.skip_ws
499
+ until @str.skip(/\]/)
500
+ if @str.scan_lit_string
501
+ values << { :string => @str[1] } # unescape escaped chars?
502
+ elsif @str.scan_lit_float
503
+ values << { :float => @str[1] }
504
+ elsif @str.scan_lit_integer
505
+ values << { :int => @str[1] }
506
+ elsif @str.skip_nil
507
+ values << { :nil => true }
508
+ elsif @str.scan_lit_bool
509
+ values << { :bool => @str[1].upcase.eql?('TRUE') }
510
+ elsif @str.scan_literal
511
+ values << { :int => @str[1] } # Assume a constant
512
+ else
513
+ syntax_error "Unrecognised array value"
514
+ end
515
+ @str.skip_ws
516
+ @str.skip(/,/)
517
+ @str.skip_ws
518
+ end
519
+ stack.last.classes.push(C_Array.new(name, values, stack.last))
520
+ p [ :create_array, values ]
521
+ else
522
+ syntax_error "Arrays should be in the form: [value1, value2, ... valueN]"
523
+ end
524
+ elsif txt = @str.get_byte # Spare chars
525
+ if state.in_func
526
+ func.text += txt
527
+ else
528
+ syntax_error "Invalid character #{txt}"
529
+ end
530
+ end
446
531
  end
447
- stack.last.classes.push(klass.new(name, string, stack.last)) if klass
448
- elsif state.in_func == false && @str.skip(/(array)(?= )/x)
449
- type = @str[1]
450
- @str.skip(/\s+/)
451
- name = @str.scan(/[A-Z][a-z_0-9A-Z]*/)
452
- @str.skip(/\s*=\s*/)
453
- values = []
454
- if @str.skip(/\[/)
455
- @str.skip(/\s+/)
456
- until @str.skip(/\s*\]/)
457
- if @str.skip(/(".*?[^\\]")/)
458
- values << { :string => @str[1] } # unescape escaped chars?
459
- elsif @str.skip(/(\d[.]\d+)/)
460
- values << { :float => @str[1] }
461
- elsif @str.skip(/(\d+)/)
462
- values << { :int => @str[1] }
463
- elsif @str.skip(/NULL/)
464
- values << { :nil => true }
465
- elsif @str.skip(/(TRUE|FALSE)/)
466
- values << { :bool => @str[1] == 'TRUE' }
467
- elsif @str.skip(/([A-Za-z0-9_]+)/)
468
- values << { :int => @str[1] } # Assume a constant
469
- else
470
- syntax_error "Unrecognised array value"
471
- end
472
- @str.skip(/\s*,\s*/)
473
- end
474
- stack.last.classes.push(C_Array.new(name, values, stack.last))
475
- p [ :create_array, values ]
476
- else
477
- syntax_error "Arrays should be in the form: [value1, value2, ... valueN]"
478
- end
479
- elsif txt = @str.get_byte # Spare chars
480
- if state.in_func
481
- func.text += txt
532
+ end
533
+ def current_line
534
+ count = 0
535
+ @string[0..@str.pos].each_byte { |b| count += 1 if b == 10 }
536
+ count
537
+ end
538
+ def syntax_error(message)
539
+ STDERR.puts "Syntax Error: #{message} at line #{current_line}\n"
540
+ if @str.rest.size > 255
541
+ STDERR.puts @str.rest[0..255]+"..."
482
542
  else
483
- syntax_error "Invalid character #{txt}"
543
+ STDERR.puts @str.rest
484
544
  end
545
+ exit 1
546
+ end
547
+ def misc_error(message, show_location=true)
548
+ STDERR.puts "Error: #{message} at line #{current_line}\n"
549
+ if show_location
550
+ if @str.rest.size > 255
551
+ STDERR.puts @str.rest[0..255]+"..."
552
+ else
553
+ STDERR.puts @str.rest
554
+ end
555
+ end
556
+ exit 1
485
557
  end
486
- end
487
- end
488
- def current_line
489
- count = 0
490
- @string[0..@str.pos].each_byte { |b| count += 1 if b == 10 }
491
- count
492
- end
493
- def syntax_error(message)
494
- STDERR.puts "Syntax Error: #{message} at line #{current_line}\n"
495
- if @str.rest.size > 255
496
- STDERR.puts @str.rest[0..255]+"..."
497
- else
498
- STDERR.puts @str.rest
499
- end
500
- exit 1
501
- end
502
- def misc_error(message, show_location=true)
503
- STDERR.puts "Error: #{message} at line #{current_line}\n"
504
- if show_location
505
- if @str.rest.size > 255
506
- STDERR.puts @str.rest[0..255]+"..."
507
- else
508
- STDERR.puts @str.rest
509
- end
510
- end
511
- exit 1
512
- end
513
558
 
514
- end
559
+ end
515
560
 
516
561
  end # m Rubber
@@ -0,0 +1,13 @@
1
+ module Rubber
2
+ class Tokenizer
3
+ def initialize(file)
4
+ @file = file
5
+ @lines = IO.readlines(@file)
6
+ @str = StringScanner.new(@string = @lines.join)
7
+ end
8
+
9
+ def next_token
10
+ @str.scan(/%[a-z_]+/)
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  module Rubber
2
- VERSION = [0,0,21]
2
+ VERSION = [0,0,22]
3
3
  def VERSION.to_s
4
4
  self.map{|i|i.to_s}.join('.')
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubber-generate
3
3
  version: !ruby/object:Gem::Version
4
- hash: 53
4
+ hash: 51
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 21
10
- version: 0.0.21
9
+ - 22
10
+ version: 0.0.22
11
11
  platform: ruby
12
12
  authors:
13
13
  - Geoff Youngs
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-08-03 00:00:00 Z
18
+ date: 2013-10-29 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description: " rubber-c-binder allows a rubyish means of generating bindings for C libraries,\n including (but not limited to) GObject based libraries.\n\n It allows C code to be written in the context of a ruby style class/method layout\n and eases type checking and conversion between Ruby & C datatypes.\n"
@@ -28,6 +28,7 @@ extra_rdoc_files:
28
28
  - README.textile
29
29
  files:
30
30
  - bin/rubber-generate
31
+ - lib/rubber/tokens.rb
31
32
  - lib/rubber/codegen/param.rb
32
33
  - lib/rubber/codegen/ginterface.rb
33
34
  - lib/rubber/codegen/genum.rb