rubber-generate 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
@@ -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