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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/README.md +122 -0
- data/Rakefile +1 -0
- data/divine.gemspec +22 -0
- data/lib/divine.rb +22 -0
- data/lib/divine/code_generators/code_generator.rb +49 -0
- data/lib/divine/code_generators/java.rb +414 -0
- data/lib/divine/code_generators/javascript.rb +512 -0
- data/lib/divine/code_generators/ruby.rb +360 -0
- data/lib/divine/dsl.rb +153 -0
- data/lib/divine/version.rb +3 -0
- metadata +90 -0
@@ -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
|