divine 0.0.1

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.
@@ -0,0 +1,360 @@
1
+ module Divine
2
+
3
+ class RubyHelperMethods < BabelHelperMethods
4
+ def ruby_base_class_template_str
5
+ %q{
6
+ class BabelBase<%= toplevel_class %>
7
+ public
8
+ def serialize
9
+ out = []
10
+ serialize_internal(out)
11
+ out.flatten.pack("C*")
12
+ end
13
+
14
+ protected
15
+ ### Read methods ###
16
+ def read_int8(data)
17
+ data.getbyte
18
+ end
19
+
20
+ def read_int16(data)
21
+ (data.getbyte << 8) | read_int8(data)
22
+ end
23
+
24
+ def read_int24(data)
25
+ (data.getbyte << 16) | read_int16(data)
26
+ end
27
+
28
+ def read_int32(data)
29
+ (data.getbyte << 24) | read_int24(data)
30
+ end
31
+
32
+ def read_bool(data)
33
+ read_int8(data) == 1
34
+ end
35
+
36
+ def read_string(data)
37
+ data.read(read_int16(data)).force_encoding('UTF-8')
38
+ end
39
+
40
+ def read_binary(data)
41
+ data.read(read_int32(data))
42
+ end
43
+
44
+ def read_short_binary(data)
45
+ data.read(read_int8(data))
46
+ end
47
+
48
+ def read_ip_number(data)
49
+ read_short_binary(data).bytes.to_a.join('.')
50
+ end
51
+
52
+ ### Write methods ###
53
+ def write_int8(v, out)
54
+ v = v.to_i
55
+ raise_error "Too large int8 number: #{v}" if v > 0xFF # Max 255
56
+ out << v
57
+ end
58
+
59
+ def write_int16(v, out)
60
+ v = v.to_i
61
+ raise_error "Too large int16 number: #{v}" if v > 0xFFFF # Max 65.535
62
+ write_int8( v >> 8 & 0xFF, out)
63
+ write_int8( v & 0xFF, out)
64
+ end
65
+
66
+ def write_int24(v, out)
67
+ v = v.to_i
68
+ raise_error "Too large int24 number: #{v}" if v > 0xFFFFFF # Max 16.777.215
69
+ write_int8( v >> 16 & 0xFF, out)
70
+ write_int16( v & 0xFFFF, out)
71
+ end
72
+
73
+ def write_int32(v, out)
74
+ v = v.to_i
75
+ raise_error "Too large int32 number: #{v}" if v > 0xFFFFFFFF # Max 4.294.967.295
76
+ write_int8( v >> 24 & 0xFF, out)
77
+ write_int24( v & 0xFFFFFF, out)
78
+ end
79
+
80
+ def write_bool(v, out)
81
+ write_int8(v ? 1 : 0, out)
82
+ end
83
+
84
+ def write_string(v, out)
85
+ s = force_to_utf8_string(v)
86
+ raise_error "Too large string: #{s.bytesize} bytes" if s.bytesize > 0xFFFF
87
+ write_int16(s.bytesize, out)
88
+ out << s.bytes.to_a
89
+ end
90
+
91
+ def write_binary(v, out)
92
+ if v.is_a?(Array)
93
+ raise_error "Too large binary: #{v.size} (#{v.class.name})" unless v.size < 0xFFFFFFFF
94
+ write_int32(v.size, out)
95
+ v.each do |x|
96
+ write_int8(x, out)
97
+ end
98
+ elsif v.is_a?(String)
99
+ raise_error "Too large binary: #{v.size} (#{v.class.name})" unless v.size < 0xFFFFFFFF
100
+ write_int32(v.size, out)
101
+ out << v.bytes.to_a
102
+ else
103
+ raise_error "Unsupported binary 'nil'" if v == nil
104
+ raise_error "Unsupported binary of type '#{v.class.name}'"
105
+ end
106
+ end
107
+
108
+ def write_short_binary(v, out)
109
+ if v.is_a?(Array)
110
+ raise_error "Too large short_binary: #{v.size} (#{v.class.name})" unless v.size < 0xFF
111
+ write_int8(v.size, out)
112
+ v.each do |x|
113
+ write_int8(x, out)
114
+ end
115
+ elsif v.is_a?(String)
116
+ raise_error "To large short_binary: #{v.size} (#{v.class.name})" unless v.size < 0xFF
117
+ write_int8(v.size, out)
118
+ out << v.bytes.to_a
119
+ else
120
+ raise_error "Unsupported binary 'nil'" if v == nil
121
+ raise_error "Unsupported binary of type '#{v.class.name}'"
122
+ end
123
+ end
124
+
125
+ def write_ip_number(v, out)
126
+ if v.is_a?(Array)
127
+ raise_error "Unknown IP v4 number #{v}" unless v.size == 0 || v.size == 4 # Only IPv4 for now
128
+ write_short_binary(v, out)
129
+ elsif v.is_a?(String)
130
+ ss = v.split(/\./).map do |s|
131
+ s.to_i
132
+ end
133
+ write_ip_number(ss, out)
134
+ else
135
+ raise_error "Unknown IP number '#{v}'"
136
+ end
137
+ end
138
+
139
+ private
140
+ def raise_error(msg)
141
+ raise "[#{self.class.name}] #{msg}"
142
+ end
143
+
144
+ def force_to_utf8_string(string)
145
+ if string.encoding != Encoding::UTF_8
146
+ string = string.encode(Encoding::UTF_8)
147
+ end
148
+ return string
149
+ end
150
+ end
151
+ }
152
+ end
153
+
154
+ def ruby_class_template_str
155
+ %q{
156
+ class <%= c.name %> < BabelBase
157
+ <% unless c.fields.empty? %>
158
+ attr_accessor <%= c.fields.map { |x| ":#{x.name}" }.join(', ') %>
159
+
160
+ def initialize()
161
+ super
162
+ <% c.fields.each do |f| %>
163
+ @<%= f.name %> ||= <%= this.ruby_get_empty_declaration(f) %>
164
+ <% end %>
165
+ end
166
+ <% end %>
167
+
168
+ def serialize_internal(out)
169
+ print "+"
170
+ <% c.simple_fields.each do |f| %>
171
+ write_<%= f.type %>(<%= f.name %>, out)
172
+ <% end %>
173
+ <% c.complex_fields.each do |f| %>
174
+ <%= this.ruby_serialize_complex f %>
175
+ <% end %>
176
+ end
177
+
178
+ def deserialize(data)
179
+ print "-"
180
+ <% c.simple_fields.each do |f| %>
181
+ @<%= f.name %> = read_<%= f.type %>(data)
182
+ <% end %>
183
+ <% c.complex_fields.each do |f| %>
184
+ <%= this.ruby_deserialize_complex f %>
185
+ <% end %>
186
+ end
187
+ end
188
+ }
189
+ end
190
+
191
+ def ruby_get_empty_declaration(field)
192
+ case field.type
193
+ when :list, :binary, :short_binary
194
+ "[]"
195
+ when :map
196
+ "{}"
197
+ when :int8, :int16, :int32
198
+ "0"
199
+ when :string, :ip_number
200
+ "\"\""
201
+ else
202
+ raise "Unkown field type #{field.type}"
203
+ end
204
+ end
205
+
206
+ def ruby_serialize_complex(field)
207
+ types = field.referenced_types
208
+ as = [
209
+ "# Serialize #{field.type} '#{field.name}'",
210
+ ruby_serialize_internal(field.name, types)
211
+ ]
212
+ format_src(6, 3, as)
213
+ end
214
+
215
+ def ruby_serialize_internal(var, types)
216
+ if types.respond_to? :first
217
+ case types.first
218
+ when :list
219
+ nv = get_fresh_variable_name
220
+ return [
221
+ "write_int32(#{var}.size, out)",
222
+ "#{var}.each do |#{nv}|",
223
+ :indent,
224
+ ruby_serialize_internal(nv, types[1]),
225
+ :deindent,
226
+ "end"
227
+ ]
228
+ when :map
229
+ nv1 = get_fresh_variable_name
230
+ nv2 = get_fresh_variable_name
231
+ return [
232
+ "write_int32(#{var}.size, out)",
233
+ "#{var}.each_pair do |#{nv1}, #{nv2}|",
234
+ :indent,
235
+ ruby_serialize_internal(nv1, types[1]),
236
+ ruby_serialize_internal(nv2, types[2]),
237
+ :deindent,
238
+ "end"
239
+ ]
240
+ else
241
+ raise "Missing serialization for #{var}"
242
+ end
243
+ else
244
+ if $all_structs[types]
245
+ "#{var}.serialize_internal(out)"
246
+
247
+ elsif $available_types[types] && $available_types[types].ancestors.include?(SimpleDefinition)
248
+ "write_#{types}(#{var}, out)"
249
+
250
+ else
251
+ raise "Missing code generation case #{types}"
252
+ end
253
+ end
254
+ end
255
+
256
+ def ruby_deserialize_complex(field)
257
+ types = field.referenced_types
258
+ as = [
259
+ "# Deserialize #{field.type} '#{field.name}'",
260
+ ruby_deserialize_internal("@#{field.name}", types)
261
+ ]
262
+ format_src(6, 3, as)
263
+ end
264
+
265
+ def ruby_deserialize_internal(var, types)
266
+ if types.respond_to? :first
267
+ case types.first
268
+ when :list
269
+ count = get_fresh_variable_name
270
+ nv = get_fresh_variable_name
271
+ return [
272
+ "#{var} = []",
273
+ "#{count} = read_int32(data)",
274
+ "(1..#{count}).each do",
275
+ :indent,
276
+ ruby_deserialize_internal(nv, types[1]),
277
+ "#{var} << #{nv}",
278
+ :deindent,
279
+ "end"
280
+ ]
281
+ when :map
282
+ count = get_fresh_variable_name
283
+ nv1 = get_fresh_variable_name
284
+ nv2 = get_fresh_variable_name
285
+ return ["#{var} = {}",
286
+ "#{count} = read_int32(data)",
287
+ "(1..#{count}).each do",
288
+ :indent,
289
+ ruby_deserialize_internal(nv1, types[1]),
290
+ ruby_deserialize_internal(nv2, types[2]),
291
+ "#{var}[#{nv1}] = #{nv2}",
292
+ :deindent,
293
+ "end"
294
+ ]
295
+ else
296
+ raise "Missing serialization for #{var}"
297
+ end
298
+ else
299
+ case types
300
+ when :map
301
+ "#{var} = {}"
302
+ when :list
303
+ "#{var} = []"
304
+ else
305
+ if $all_structs.key? types
306
+ [
307
+ "#{var} = #{types}.new",
308
+ "#{var}.deserialize(data)"
309
+ ]
310
+ else
311
+ "#{var} = read_#{types}(data)"
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end
317
+
318
+
319
+
320
+ class RubyGenerator < RubyHelperMethods
321
+ @debug = true
322
+
323
+ def generate_code(structs, opts)
324
+ pp opts
325
+ base_template = Erubis::Eruby.new(ruby_base_class_template_str)
326
+ class_template = Erubis::Eruby.new(ruby_class_template_str)
327
+ keys = structs.keys.sort
328
+ src = keys.map do |k|
329
+ ss = structs[k]
330
+ # TODO: Should we merge different versions and deduce deprecated methods, warn for incompatible changes, etc?
331
+ raise "Duplicate definitions of struct #{k}" if ss.size > 1
332
+ class_template.result( c: ss.first, this: self )
333
+ end
334
+
335
+ # User defined super class?
336
+ toplevel = opts[:parent_class] || nil
337
+ toplevel = " < #{toplevel}" if toplevel
338
+ return "#{ruby_get_begin_module(opts)}#{base_template.result({ toplevel_class: toplevel })}\n\n#{src.join("\n\n")}#{ruby_get_end_module(opts)}"
339
+ end
340
+
341
+ def ruby_get_begin_module(opts)
342
+ if opts[:module]
343
+ "module #{opts[:module]}\n\n"
344
+ else
345
+ nil
346
+ end
347
+ end
348
+
349
+ def ruby_get_end_module(opts)
350
+ if opts[:module]
351
+ "\n\nend\n"
352
+ else
353
+ nil
354
+ end
355
+ end
356
+ end
357
+
358
+
359
+ $language_generators[:ruby] = RubyGenerator.new
360
+ end
data/lib/divine/dsl.rb ADDED
@@ -0,0 +1,153 @@
1
+ module Divine
2
+ $all_structs = {}
3
+
4
+ class StructDefinition
5
+ def initialize(type, args)
6
+ @type = type
7
+ @args = args
8
+ end
9
+
10
+ def name
11
+ @args.first
12
+ end
13
+
14
+ def type
15
+ @type
16
+ end
17
+ end
18
+
19
+ class SimpleDefinition < StructDefinition
20
+ def simple?; true; end
21
+ def referenced_types
22
+ @type
23
+ end
24
+ end
25
+
26
+ class ComplexDefinition < StructDefinition
27
+ def simple?; false; end
28
+ def referenced_types
29
+ fs = referenced_types_internal.map do |t|
30
+ if t.is_a? StructBuilder
31
+ t.fields.map do |f|
32
+ f.referenced_types
33
+ end
34
+ else
35
+ t.to_sym
36
+ end
37
+ end
38
+ return [type] + fs.flatten(1)
39
+ end
40
+ end
41
+
42
+ class Integer32Definition < SimpleDefinition
43
+ end
44
+
45
+ class Integer24Definition < SimpleDefinition
46
+ end
47
+
48
+ class Integer16Definition < SimpleDefinition
49
+ end
50
+
51
+ class Integer8Definition < SimpleDefinition
52
+ end
53
+
54
+ class BinaryDefinition < SimpleDefinition
55
+ end
56
+
57
+ class ShortBinaryDefinition < SimpleDefinition # Shorted than 256 bytes
58
+ end
59
+
60
+ class StringDefinition < SimpleDefinition
61
+ end
62
+
63
+ class BooleanDefinition < SimpleDefinition
64
+ end
65
+
66
+ class IpNumberDefinition < SimpleDefinition
67
+ end
68
+
69
+
70
+ class ListDefinition < ComplexDefinition
71
+ protected
72
+ def referenced_types_internal
73
+ [@args[1]]
74
+ end
75
+ end
76
+
77
+ class MapDefinition < ComplexDefinition
78
+ protected
79
+ def referenced_types_internal
80
+ [@args[1], @args[2]]
81
+ end
82
+ end
83
+
84
+
85
+
86
+ $available_types = {
87
+ int32: Integer32Definition,
88
+ int24: Integer24Definition,
89
+ int16: Integer16Definition,
90
+ int8: Integer8Definition,
91
+ binary: BinaryDefinition,
92
+ short_binary: ShortBinaryDefinition,
93
+ string: StringDefinition,
94
+ bool: BooleanDefinition,
95
+ list: ListDefinition,
96
+ map: MapDefinition,
97
+ ip_number: IpNumberDefinition
98
+ }
99
+
100
+
101
+ class StructBuilder
102
+ # Name = name of the struct
103
+ # Version = struct version (not currently used)
104
+ # Fields = All defined fields
105
+ attr_reader :name, :version, :fields
106
+
107
+ def initialize(name, version)
108
+ @version = version
109
+ @name = name.to_sym
110
+ @fields = []
111
+ unless @name == :_inline_
112
+ $all_structs[@name] ||= []
113
+ $all_structs[@name] << self
114
+ end
115
+ end
116
+
117
+ #
118
+ # Get all simple fields, i.e. basic types like string, etc
119
+ #
120
+ def simple_fields
121
+ fields.select(&:simple?)
122
+ end
123
+
124
+ #
125
+ # Get all complex fields, i.e. lists and hashmaps
126
+ #
127
+ def complex_fields
128
+ fields.reject(&:simple?)
129
+ end
130
+
131
+ def method_missing(m, *args, &block)
132
+ puts "... #{m}"
133
+ type = $available_types[m]
134
+ if type
135
+ if block_given?
136
+ puts ".... recursive definition"
137
+ builder = StructBuilder.new(:_inline_, version)
138
+ Docile.dsl_eval(builder, &block)
139
+ args << builder
140
+ end
141
+ if @name == :_inline_
142
+ # Pad the _inline_ name to anonymous inner types
143
+ @fields << type.new(m, [@name] + args)
144
+ else
145
+ @fields << type.new(m, args)
146
+ end
147
+ #puts "... adding #{m} to #{name}, got #{fields} ..."
148
+ else
149
+ super.send(m, args, block)
150
+ end
151
+ end
152
+ end
153
+ end