protobuf-generate 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/Gemfile +2 -0
- data/LICENSE +10 -0
- data/README.md +56 -0
- data/bin/protobuf-generate +44 -0
- data/lib/protobuf/generate/language/c/c.erb +502 -0
- data/lib/protobuf/generate/language/c/h.erb +71 -0
- data/lib/protobuf/generate/language/c.rb +39 -0
- data/lib/protobuf/generate/language.rb +64 -0
- data/lib/protobuf/generate/parser.rb +92 -0
- data/lib/protobuf-generate.rb +2 -0
- data/protobuf-generate.gemspec +22 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 78cf665e01a339bcdf515cee143af388685ef34a
|
4
|
+
data.tar.gz: 268a8fa0d167f93f8c86ec0f4f9ad304bb4650d2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 30773411e397dbb87a547de33809646493fd3350c83c2fca83c6a2f9e4ed7a64a792780aa626ee60c7febea25d16d973da8550c5a6b4034196f8c46cbdd7c56f
|
7
|
+
data.tar.gz: ca1613fe95fa0e2a3a02c2616f2b23c19089e7d71a8bc63caed895907a7d6f4d3ba67cdc2f159e411ffb1728e3ccb02eee4b08e154c3ea8087aee45d35855a7c
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
NOTE: Fuck restrictive 'open' source licenses honestly. You can pretty much do whatever the fuck you want with my code under MIT but the C templates [ch].erb are probably considered derivative works because I stole them originally from from the Apache 2 (http://www.apache.org/licenses/LICENSE-2.0) licensed https://code.google.com/p/protobuf-embedded-c
|
2
|
+
|
3
|
+
Copyright (C) 2013 Shane Hanna
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
10
|
+
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# protobuf-generate
|
2
|
+
|
3
|
+
[source](https://bitbucket.org/shanehanna/protobuf-generate)
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
A multi-language concrete protobuf code generator in Ruby.
|
8
|
+
|
9
|
+
## Synopsis
|
10
|
+
|
11
|
+
A simple PEG parser, AST and template based approach to code generation.
|
12
|
+
|
13
|
+
[protobuf-embedded-c](https://code.google.com/p/protobuf-embedded-c/) uses the same approach but requires a page of
|
14
|
+
rage inducing instructions to install obsolete Eclipse plugins and Java bullshit if you want to be able to develop
|
15
|
+
and customize any of the C it generates. Ruby and scripting languages in general are ideal for this sort of thing
|
16
|
+
so that's what I've done.
|
17
|
+
|
18
|
+
## Install
|
19
|
+
|
20
|
+
```
|
21
|
+
gem install protobuf-generate
|
22
|
+
```
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
```
|
27
|
+
protobuf-generate <language> <protofile>
|
28
|
+
```
|
29
|
+
|
30
|
+
## Languages
|
31
|
+
|
32
|
+
### C - Embedded
|
33
|
+
|
34
|
+
C99+ suitable for embedded systems.
|
35
|
+
|
36
|
+
#### string and byte
|
37
|
+
|
38
|
+
A fixed size string and byte structure is generated using a required @size_max meta attribute to avoid dynamic
|
39
|
+
allocation. Strings sent from other protobuf implementations that are too long for the structure will be truncated and
|
40
|
+
null terminated to fit within the maximum size. Bytes will just be truncated.
|
41
|
+
|
42
|
+
```
|
43
|
+
message Message {
|
44
|
+
required string name = 1; // @size_max = 32
|
45
|
+
}
|
46
|
+
```
|
47
|
+
|
48
|
+
```
|
49
|
+
typedef struct message_t {
|
50
|
+
struct {
|
51
|
+
uint8_t data[32];
|
52
|
+
site_t size;
|
53
|
+
} name;
|
54
|
+
} message_t;
|
55
|
+
```
|
56
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
3
|
+
require 'protobuf-generate'
|
4
|
+
|
5
|
+
# Usage.
|
6
|
+
unless ARGV.size == 2
|
7
|
+
puts %q{
|
8
|
+
Generate native code from protobuf files.
|
9
|
+
|
10
|
+
usage: protobuf-generate <language> <proto>
|
11
|
+
language - Native language to generate.
|
12
|
+
proto - The protobuf file to generate from.
|
13
|
+
}
|
14
|
+
exit 0
|
15
|
+
end
|
16
|
+
|
17
|
+
unless generator = Protobuf::Generate::Language.find(ARGV[0])
|
18
|
+
puts 'ERROR: Unknown language "%s".' % ARGV[0]
|
19
|
+
exit -1
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
proto = ARGV[1]
|
24
|
+
tree = Protobuf::Generate::Parser.new.parse(File.read(proto))
|
25
|
+
ast = Protobuf::Generate::Transform.new.apply(tree)
|
26
|
+
gen = generator.new(ast)
|
27
|
+
|
28
|
+
# TODO: Think about how best to have generator deal with multiple templates. e.g. *.c and *.h
|
29
|
+
gen.templates.each do |template|
|
30
|
+
# TODO: Each language should take care of file naming conventions as well.
|
31
|
+
postfix = File.basename(template).gsub(/\.erb$/, '') # c.erb, h.erb, demo.c.erb becomes .c, .h, demo.c
|
32
|
+
package = ast.find{|e| e.kind_of?(Protobuf::Generate::Ast::Package)}
|
33
|
+
package = package ? package.name : File.basename(proto).gsub(/\.proto$/)
|
34
|
+
package = package.gsub('.', '_').gsub(/([^A-Z_])([A-Z]+)/, '\1_\2').gsub(/_+/, '_').downcase
|
35
|
+
filename = File.join(File.dirname(proto), [package, postfix].join('.'))
|
36
|
+
|
37
|
+
File.open(filename, File::CREAT | File::TRUNC | File::WRONLY) do |fh|
|
38
|
+
fh.puts gen.generate(template, filename)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue Parslet::ParseFailed => error
|
42
|
+
puts error.cause.ascii_tree
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,502 @@
|
|
1
|
+
#include "<%= filename.sub(/\.c$/, '.h') %>" <%# Hack job. %>
|
2
|
+
|
3
|
+
#include <stdbool.h>
|
4
|
+
#include <stdint.h>
|
5
|
+
#include <string.h>
|
6
|
+
|
7
|
+
static int write_raw_byte(uint8_t value, uint8_t *buffer, int offset) {
|
8
|
+
*(buffer + offset) = value;
|
9
|
+
return ++offset;
|
10
|
+
}
|
11
|
+
|
12
|
+
static int write_raw_little_endian32(uint32_t value, uint8_t *buffer, int offset) {
|
13
|
+
offset = write_raw_byte((uint8_t)((value ) & 0xFF), buffer, offset);
|
14
|
+
offset = write_raw_byte((uint8_t)((value >> 8) & 0xFF), buffer, offset);
|
15
|
+
offset = write_raw_byte((uint8_t)((value >> 16) & 0xFF), buffer, offset);
|
16
|
+
offset = write_raw_byte((uint8_t)((value >> 24) & 0xFF), buffer, offset);
|
17
|
+
return offset;
|
18
|
+
}
|
19
|
+
|
20
|
+
static int write_raw_little_endian64(uint64_t value, uint8_t *buffer, int offset) {
|
21
|
+
offset = write_raw_byte((uint8_t)((value ) & 0xFF), buffer, offset);
|
22
|
+
offset = write_raw_byte((uint8_t)((value >> 8) & 0xFF), buffer, offset);
|
23
|
+
offset = write_raw_byte((uint8_t)((value >> 16) & 0xFF), buffer, offset);
|
24
|
+
offset = write_raw_byte((uint8_t)((value >> 24) & 0xFF), buffer, offset);
|
25
|
+
offset = write_raw_byte((uint8_t)((value >> 32) & 0xFF), buffer, offset);
|
26
|
+
offset = write_raw_byte((uint8_t)((value >> 40) & 0xFF), buffer, offset);
|
27
|
+
offset = write_raw_byte((uint8_t)((value >> 48) & 0xFF), buffer, offset);
|
28
|
+
offset = write_raw_byte((uint8_t)((value >> 56) & 0xFF), buffer, offset);
|
29
|
+
return offset;
|
30
|
+
}
|
31
|
+
|
32
|
+
static int write_raw_varint32(uint32_t value, uint8_t *buffer, int offset) {
|
33
|
+
while (1) {
|
34
|
+
if ((value & ~0x7F) == 0) {
|
35
|
+
offset = write_raw_byte((uint8_t)value, buffer, offset);
|
36
|
+
return offset;
|
37
|
+
} else {
|
38
|
+
offset = write_raw_byte((uint8_t)((value & 0x7F) | 0x80), buffer, offset);
|
39
|
+
value = value >> 7;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
return offset;
|
43
|
+
}
|
44
|
+
|
45
|
+
static int write_raw_varint64(uint64_t value, uint8_t *buffer, int offset) {
|
46
|
+
while (1) {
|
47
|
+
if ((value & ~0x7FL) == 0) {
|
48
|
+
offset = write_raw_byte((uint8_t)value, buffer, offset);
|
49
|
+
return offset;
|
50
|
+
} else {
|
51
|
+
offset = write_raw_byte((uint8_t)((value & 0x7F) | 0x80), buffer, offset);
|
52
|
+
value = value >> 7;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
return offset;
|
56
|
+
}
|
57
|
+
|
58
|
+
static int write_raw_bytes(uint8_t *bytes, int bytes_size, uint8_t *buffer, int offset) {
|
59
|
+
for (int i = 0; i < bytes_size; ++i)
|
60
|
+
offset = write_raw_byte(*(bytes + i), buffer, offset);
|
61
|
+
return offset;
|
62
|
+
}
|
63
|
+
|
64
|
+
static inline uint32_t encode_zig_zag32(int32_t n) {
|
65
|
+
return (n << 1) ^ (n >> 31);
|
66
|
+
}
|
67
|
+
|
68
|
+
static inline uint64_t encode_zig_zag64(int64_t n) {
|
69
|
+
return (n << 1) ^ (n >> 63);
|
70
|
+
}
|
71
|
+
|
72
|
+
static inline int32_t decode_zig_zag32(uint32_t n) {
|
73
|
+
return (n >> 1) ^ -(n & 1);
|
74
|
+
}
|
75
|
+
|
76
|
+
static inline int64_t decode_zig_zag64(uint64_t n) {
|
77
|
+
return (n >> 1) ^ -(n & 1);
|
78
|
+
}
|
79
|
+
|
80
|
+
static inline int read_raw_byte(uint8_t *tag, const uint8_t *buffer, int offset) {
|
81
|
+
*tag = *(buffer + offset);
|
82
|
+
return ++offset;
|
83
|
+
}
|
84
|
+
|
85
|
+
static int read_raw_little_endian32(uint32_t *tag, const uint8_t *buffer, int offset) {
|
86
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
87
|
+
uint8_t b1 = (uint8_t)*tag;
|
88
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
89
|
+
uint8_t b2 = (uint8_t)*tag;
|
90
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
91
|
+
uint8_t b3 = (uint8_t)*tag;
|
92
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
93
|
+
uint8_t b4 = (uint8_t)*tag;
|
94
|
+
|
95
|
+
*tag = (((uint32_t)b1 & 0xff) ) |
|
96
|
+
(((uint32_t)b2 & 0xff) << 8) |
|
97
|
+
(((uint32_t)b3 & 0xff) << 16) |
|
98
|
+
(((uint32_t)b4 & 0xff) << 24);
|
99
|
+
return offset;
|
100
|
+
}
|
101
|
+
|
102
|
+
static int read_raw_little_endian64(uint64_t *tag, const uint8_t *buffer, int offset) {
|
103
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
104
|
+
uint8_t b1 = (uint8_t)*tag;
|
105
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
106
|
+
uint8_t b2 = (uint8_t)*tag;
|
107
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
108
|
+
uint8_t b3 = (uint8_t)*tag;
|
109
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
110
|
+
uint8_t b4 = (uint8_t)*tag;
|
111
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
112
|
+
uint8_t b5 = (uint8_t)*tag;
|
113
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
114
|
+
uint8_t b6 = (uint8_t)*tag;
|
115
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
116
|
+
uint8_t b7 = (uint8_t)*tag;
|
117
|
+
offset = read_raw_byte((uint8_t *)tag, buffer, offset);
|
118
|
+
uint8_t b8 = (uint8_t)*tag;
|
119
|
+
|
120
|
+
*tag = (((uint64_t)b1 & 0xff) ) |
|
121
|
+
(((uint64_t)b2 & 0xff) << 8) |
|
122
|
+
(((uint64_t)b3 & 0xff) << 16) |
|
123
|
+
(((uint64_t)b4 & 0xff) << 24) |
|
124
|
+
(((uint64_t)b5 & 0xff) << 32) |
|
125
|
+
(((uint64_t)b6 & 0xff) << 40) |
|
126
|
+
(((uint64_t)b7 & 0xff) << 48) |
|
127
|
+
(((uint64_t)b8 & 0xff) << 56);
|
128
|
+
|
129
|
+
return offset;
|
130
|
+
}
|
131
|
+
|
132
|
+
static int read_raw_varint32(uint32_t *tag, const uint8_t *buffer, int offset) {
|
133
|
+
uint8_t result;
|
134
|
+
|
135
|
+
offset = read_raw_byte(&result, buffer, offset);
|
136
|
+
if (result >= 0) {
|
137
|
+
*tag = result;
|
138
|
+
return offset;
|
139
|
+
}
|
140
|
+
*tag = result & 0x7f;
|
141
|
+
offset = read_raw_byte(&result, buffer, offset);
|
142
|
+
if (result >= 0) {
|
143
|
+
*tag |= result << 7;
|
144
|
+
} else {
|
145
|
+
*tag |= (result & 0x7f) << 7;
|
146
|
+
offset = read_raw_byte(&result, buffer, offset);
|
147
|
+
if (result >= 0) {
|
148
|
+
*tag |= result << 14;
|
149
|
+
} else {
|
150
|
+
*tag |= (result & 0x7f) << 14;
|
151
|
+
offset = read_raw_byte(&result, buffer, offset);
|
152
|
+
if (result >= 0) {
|
153
|
+
*tag |= ((uint32_t)result) << 21;
|
154
|
+
} else {
|
155
|
+
*tag |= (((uint32_t)result) & 0x7f) << 21;
|
156
|
+
offset = read_raw_byte(&result, buffer, offset);
|
157
|
+
*tag |= ((uint32_t)result) << 28;
|
158
|
+
if (result < 0) {
|
159
|
+
/* Discard upper 32 bits. */
|
160
|
+
int i;
|
161
|
+
for (i = 0; i < 5; ++ i) {
|
162
|
+
offset = read_raw_byte(&result, buffer, offset);
|
163
|
+
if (result >= 0) {
|
164
|
+
return offset;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
/* Invalid state. */
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
return offset;
|
173
|
+
}
|
174
|
+
|
175
|
+
static int read_raw_varint64(uint64_t *tag, uint8_t *buffer, int offset) {
|
176
|
+
short shift = 0;
|
177
|
+
uint8_t b;
|
178
|
+
*tag = 0;
|
179
|
+
while (shift < 64) {
|
180
|
+
offset = read_raw_byte(&b, buffer, offset);
|
181
|
+
*tag |= (uint64_t)(b & 0x7F) << shift;
|
182
|
+
if ((b & 0x80) == 0) {
|
183
|
+
return offset;
|
184
|
+
}
|
185
|
+
shift += 7;
|
186
|
+
}
|
187
|
+
/* return error code. */
|
188
|
+
return -1;
|
189
|
+
}
|
190
|
+
|
191
|
+
<% each do |exp| %>
|
192
|
+
<%
|
193
|
+
if exp.kind_of?(Protobuf::Generate::Ast::Package)
|
194
|
+
package exp.name
|
195
|
+
elsif exp.kind_of?(Protobuf::Generate::Ast::Enum)
|
196
|
+
%>
|
197
|
+
<% elsif exp.kind_of?(Protobuf::Generate::Ast::Message) %>
|
198
|
+
|
199
|
+
void <%= namespaced_function exp.name, 'clear' %>(<%= namespaced_type exp.name %> *pb) {
|
200
|
+
memset(pb, 0, sizeof(*pb));
|
201
|
+
}
|
202
|
+
|
203
|
+
void <%= namespaced_function exp.name, 'init', 'optional', 'attributes' %>(<%= namespaced_type exp.name %> *pb) {
|
204
|
+
<% exp.fields.each do |field| %>
|
205
|
+
<% if field.optional? %>
|
206
|
+
<% if type_message?(field.type) %>
|
207
|
+
<%= namespaced_function field.type, 'init', 'optional', 'attributes' %>(&pb-><%= field.name %>);
|
208
|
+
<% elsif type_enum?(field.type) %>
|
209
|
+
pb-><%= field.name %> = <%= namespaced_constant field.type, type_enum_default(field.type, field.options.fetch('default', nil)) %>;
|
210
|
+
<% elsif field.type =~ /bool/ %>
|
211
|
+
pb-><%= field.name %> = <%= field.options.fetch(:default, 'false') %>;
|
212
|
+
<% elsif field.type =~ /string/ %>
|
213
|
+
memcpy(pb-><%= field.name %>.data, <%= field.options.fetch('default', '""') %>, <%= size = (field.options.fetch('default', '').bytesize - 1); size > 0 ? size : 0 %>);
|
214
|
+
pb-><%= field.name %>.size = <%= field.options.fetch('default', '').bytesize %>;
|
215
|
+
<% elsif field.type =~ /bytes/ %>
|
216
|
+
memcpy(pb-><%= field.name %>.data, <%= field.options.fetch('default', '""') %>, <%= field.options.fetch('default', '').bytesize %>);
|
217
|
+
pb-><%= field.name %>.size = <%= field.options.fetch('default', '').bytesize %>;
|
218
|
+
<% else %>
|
219
|
+
pb-><%= field.name %> = <%= field.options.fetch('default', 0) %>;
|
220
|
+
<% end %>
|
221
|
+
<% end %>
|
222
|
+
<% end %>
|
223
|
+
return;
|
224
|
+
}
|
225
|
+
|
226
|
+
bool <%= namespaced_function exp.name, 'is', 'default', 'message' %>(const <%= namespaced_type exp.name %> *pb) {
|
227
|
+
return true
|
228
|
+
<% exp.fields.each do |field| %> // <%= field.name %>
|
229
|
+
<% if type_message?(field.type) %>
|
230
|
+
&& <%= namespaced_function field.type, 'is', 'default', 'message' %>(&pb-><%= field.name %>)
|
231
|
+
<% elsif type_enum?(field.type) %>
|
232
|
+
&& pb-><%= field.name %> == <%= namespaced_constant field.type, type_enum_default(field.type, field.options.fetch('default', nil)) %>
|
233
|
+
<% elsif field.type =~ /string|bytes/ %>
|
234
|
+
&& memcmp(&pb-><%= field.name %>.data, <%= field.options.fetch('default', '""') %>, <%= field.meta.fetch('size_max', field.options.fetch('default', '').bytesize) %>) == 0
|
235
|
+
&& pb-><%= field.name %>.size == <%= field.options.fetch('default', '').bytesize %>
|
236
|
+
<% else %>
|
237
|
+
&& pb-><%= field.name %> == <%= field.options.fetch('default', 0) %>
|
238
|
+
<% end %>
|
239
|
+
<% end %>;
|
240
|
+
}
|
241
|
+
|
242
|
+
int <%= namespaced_function exp.name, 'write' %>(const <%= namespaced_type exp.name %> *pb, uint8_t *buffer, int offset) {
|
243
|
+
<% exp.fields.each do |field| %> // <%= field.name %>
|
244
|
+
<% if field.optional? %>
|
245
|
+
<% if type_message?(field.type) %>
|
246
|
+
if (!<%= namespaced_function field.type, 'is', 'default', 'message' %>(&pb-><%= field.name %>)) {
|
247
|
+
offset = <%= namespaced_function field.type, 'write', 'with', 'tag' %>(&pb-><%= field.name %>, buffer, offset, <%= field.tag %>);
|
248
|
+
}
|
249
|
+
<% elsif type_enum?(field.type) %>
|
250
|
+
if (pb-><%= field.name %> != <%= namespaced_constant field.type, type_enum_default(field.type, field.options.fetch('default', nil)) %>) {
|
251
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+0, buffer, offset);
|
252
|
+
offset = write_raw_varint32(pb-><%= field.name %>, buffer, offset);
|
253
|
+
}
|
254
|
+
<% elsif field.type =~ /double/ %>
|
255
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
256
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
257
|
+
offset = write_raw_little_endian64(*(uint64_t *)&pb-><%= field.name %>, buffer, offset);
|
258
|
+
}
|
259
|
+
<% elsif field.type =~ /float/ %>
|
260
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
261
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
262
|
+
offset = write_raw_little_endian32(*(uint32_t *)&pb-><%= field.name %>, buffer, offset);
|
263
|
+
}
|
264
|
+
<% elsif field.type =~ /int32/%>
|
265
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
266
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
267
|
+
if (pb-><%= field.name %> >= 0) offset = write_raw_varin32(pb-><%= field.name %>, buffer, offset);
|
268
|
+
else offset = write_raw_varint64(pb-><%= field.name %>, buffer, offset);
|
269
|
+
}
|
270
|
+
<% elsif field.type =~ /int64/ %>
|
271
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
272
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
273
|
+
offset = write_raw_varint64(pb-><%= field.name %>, buffer, offset);
|
274
|
+
}
|
275
|
+
<% elsif field.type =~ /sint32/ %>
|
276
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
277
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
278
|
+
offset = write_raw_varint32(encode_zig_zag32(pb-><%= field.name %>), buffer, offset);
|
279
|
+
}
|
280
|
+
<% elsif field.type =~ /sint62/ %>
|
281
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
282
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
283
|
+
offset = write_raw_varint64(encode_zig_zag64(pb-><%= field.name %>), buffer, offset);
|
284
|
+
}
|
285
|
+
<% elsif field.type =~ /uint32/ %>
|
286
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
287
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
288
|
+
offset = write_raw_varint32(pb-><%= field.name %>, buffer, offset);
|
289
|
+
}
|
290
|
+
<% elsif field.type =~ /uint64/ %>
|
291
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
292
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
293
|
+
offset = write_raw_varint64(pb-><%= field.name %>, buffer, offset);
|
294
|
+
}
|
295
|
+
<% elsif field.type =~ /s?fixed32/ %>
|
296
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
297
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
298
|
+
offset = write_raw_little_endian32(pb-><%= field.name %>, buffer, offset);
|
299
|
+
}
|
300
|
+
<% elsif field.type =~ /s?fixed64/ %>
|
301
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 0) %>) {
|
302
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
303
|
+
offset = write_raw_little_endian64(pb-><%= field.name %>, buffer, offset);
|
304
|
+
}
|
305
|
+
<% elsif field.type =~ /bool/ %>
|
306
|
+
if (pb-><%= field.name %> != <%= field.options.fetch('default', 'false') %>) {
|
307
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
308
|
+
offset = write_raw_byte(pb-><%= field.name %>, buffer, offset);
|
309
|
+
}
|
310
|
+
<% elsif field.type =~ /string/ %>
|
311
|
+
if (memcmp(&pb-><%= field.name %>.data, <%= field.options.fetch('default', '""') %>, <%= field.meta.fetch('size_max', field.options.fetch('default', '').bytesize) %>) != 0) {
|
312
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
313
|
+
offset = write_raw_varint32(pb-><%= field.name %>.size, buffer, offset);
|
314
|
+
offset = write_raw_bytes((uint8_t *)pb-><%= field.name %>.data, pb-><%= field.name %>.size, buffer, offset);
|
315
|
+
}
|
316
|
+
<% elsif field.type =~ /bytes/ %>
|
317
|
+
if (memcmp(&pb-><%= field.name %>.data, <%= field.options.fetch('default', '""') %>, <%= field.meta.fetch('size_max', field.options.fetch('default', '').bytesize) %>) != 0) {
|
318
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
319
|
+
offset = write_raw_varint32(pb-><%= field.name %>.size, buffer, offset);
|
320
|
+
offset = write_raw_bytes((uint8_t *)pb-><%= field.name %>.data, pb-><%= field.name %>.size, buffer, offset);
|
321
|
+
}
|
322
|
+
<% end %>
|
323
|
+
<% else %>
|
324
|
+
<% if type_message?(field.type) %>
|
325
|
+
offset = <%= namespaced_function field.type, 'write', 'with', 'tag' %>(&pb-><%= field.name %>, buffer, offset, <%= field.tag %>);
|
326
|
+
<% elsif type_enum?(field.type) %>
|
327
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+0, buffer, offset);
|
328
|
+
offset = write_raw_varint32(pb-><%= field.name %>, buffer, offset);
|
329
|
+
<% elsif field.type =~ /double/ %>
|
330
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
331
|
+
offset = write_raw_little_endian64(*(uint64_t *)&pb-><%= field.name %>, buffer, offset);
|
332
|
+
<% elsif field.type =~ /float/ %>
|
333
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
334
|
+
offset = write_raw_little_endian32(*(uint32_t *)&pb-><%= field.name %>, buffer, offset);
|
335
|
+
<% elsif field.type =~ /int32/ %>
|
336
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
337
|
+
if (pb-><%= field.name %> >= 0) offset = write_raw_varin32(pb-><%= field.name %>, buffer, offset);
|
338
|
+
else offset = write_raw_varint64(pb-><%= field.name %>, buffer, offset);
|
339
|
+
<% elsif field.type =~ /int64/ %>
|
340
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
341
|
+
offset = write_raw_varint64(pb-><%= field.name %>, buffer, offset);
|
342
|
+
<% elsif field.type =~ /sint32/ %>
|
343
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
344
|
+
offset = write_raw_varint32(encode_zig_zag32(pb-><%= field.name %>), buffer, offset);
|
345
|
+
<% elsif field.type =~ /sint62/ %>
|
346
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
347
|
+
offset = write_raw_varint64(encode_zig_zag64(pb-><%= field.name %>), buffer, offset);
|
348
|
+
<% elsif field.type =~ /uint32/ %>
|
349
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
350
|
+
offset = write_raw_varint32(pb-><%= field.name %>, buffer, offset);
|
351
|
+
<% elsif field.type =~ /uint64/ %>
|
352
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
353
|
+
offset = write_raw_varint64(pb-><%= field.name %>, buffer, offset);
|
354
|
+
<% elsif field.type =~ /s?fixed32/ %>
|
355
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
356
|
+
offset = write_raw_little_endian32(pb-><%= field.name %>, buffer, offset);
|
357
|
+
<% elsif field.type =~ /s?fixed64/ %>
|
358
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
359
|
+
offset = write_raw_little_endian64(pb-><%= field.name %>, buffer, offset);
|
360
|
+
<% elsif field.type =~ /bool/ %>
|
361
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
362
|
+
offset = write_raw_byte(pb-><%= field.name %>, buffer, offset);
|
363
|
+
<% elsif field.type =~ /string/ %>
|
364
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
365
|
+
offset = write_raw_varint32(pb-><%= field.name %>.size, buffer, offset);
|
366
|
+
offset = write_raw_bytes((uint8_t *)pb-><%= field.name %>.data, pb-><%= field.name %>.size, buffer, offset);
|
367
|
+
<% elsif field.type =~ /bytes/ %>
|
368
|
+
offset = write_raw_varint32((<%= field.tag %><<3)+<%= type_wire field.type %>, buffer, offset);
|
369
|
+
offset = write_raw_varint32(pb-><%= field.name %>.size, buffer, offset);
|
370
|
+
offset = write_raw_bytes((uint8_t *)pb-><%= field.name %>.data, pb-><%= field.name %>.size, buffer, offset);
|
371
|
+
<% end %>
|
372
|
+
<% end %>
|
373
|
+
<% end %>
|
374
|
+
return offset;
|
375
|
+
}
|
376
|
+
|
377
|
+
int <%= namespaced_function exp.name, 'write', 'delimited', 'to' %>(const <%= namespaced_type exp.name %> *pb, uint8_t *buffer, int offset) {
|
378
|
+
int i, shift, new_offset, size;
|
379
|
+
|
380
|
+
new_offset = <%= namespaced_function exp.name, 'write' %>(pb, buffer, offset);
|
381
|
+
size = new_offset - offset;
|
382
|
+
shift = (size > 127) ? 2 : 1;
|
383
|
+
for (i = new_offset - 1; i >= offset; -- i)
|
384
|
+
*(buffer + i + shift) = *(buffer + i);
|
385
|
+
|
386
|
+
write_raw_varint32((uint32_t)size, buffer, offset);
|
387
|
+
return new_offset + shift;
|
388
|
+
}
|
389
|
+
|
390
|
+
int <%= namespaced_function exp.name, 'write', 'with', 'tag' %>(const <%= namespaced_type exp.name %> *pb, uint8_t *buffer, int offset, int tag) {
|
391
|
+
offset = write_raw_varint32((tag<<3)+2, buffer, offset);
|
392
|
+
offset = <%= namespaced_function exp.name, 'write', 'delimited', 'to' %>(pb, buffer, offset);
|
393
|
+
return offset;
|
394
|
+
}
|
395
|
+
|
396
|
+
int <%= namespaced_function exp.name, 'encode' %>(const <%= namespaced_type exp.name %> *pb, uint8_t *data, size_t data_size, size_t *encoded_size) {
|
397
|
+
*encoded_size = <%= namespaced_function exp.name, 'write', 'delimited', 'to' %>(pb, data, -1);
|
398
|
+
return 0; // TODO: Encoding errors.
|
399
|
+
}
|
400
|
+
|
401
|
+
int <%= namespaced_function exp.name, 'read', 'delimited', 'from' %>(<%= namespaced_type exp.name %> *pb, const uint8_t *buffer, int offset);
|
402
|
+
|
403
|
+
int <%= namespaced_function exp.name, 'read' %>(<%= namespaced_type exp.name %> *pb, const uint8_t *buffer, int offset, int limit) {
|
404
|
+
<%= namespaced_function exp.name, 'clear' %>(pb);
|
405
|
+
<%= namespaced_function exp.name, 'init', 'optional', 'attributes' %>(pb);
|
406
|
+
|
407
|
+
uint32_t tag = 0;
|
408
|
+
while (offset < limit) {
|
409
|
+
offset = read_raw_varint32(&tag, buffer, offset);
|
410
|
+
tag = tag>>3;
|
411
|
+
switch (tag) {
|
412
|
+
<% exp.fields.each do |field| %>
|
413
|
+
case <%= field.tag %>:
|
414
|
+
// <%= field.name %>
|
415
|
+
<% if type_message?(field.type) %>
|
416
|
+
offset = <%= namespaced_function field.type, 'read', 'delimited', 'from' %>(&pb-><%= field.name %>, buffer, offset);
|
417
|
+
<% elsif type_enum?(field.type) %>
|
418
|
+
offset = read_raw_varint32(&tag, buffer, offset);
|
419
|
+
pb-><%= field.name %> = tag;
|
420
|
+
<% elsif field.type =~ /float/ %>
|
421
|
+
offset = read_raw_little_endian32(&tag, buffer, offset);
|
422
|
+
pb-><%= field.name %> = *(float *)(&tag);
|
423
|
+
<% elsif field.type =~ /double/ %>
|
424
|
+
{
|
425
|
+
uint64_t value = 0;
|
426
|
+
offset = read_raw_little_endian64(&value, buffer, offset);
|
427
|
+
pb-><%= field.name %> = *(double *)(&value);
|
428
|
+
}
|
429
|
+
<% elsif field.type =~ /int32/ %>
|
430
|
+
offset = read_raw_varint32(&tag, buffer, offset);
|
431
|
+
pb-><%= field.name %> = (int32_t)tag;
|
432
|
+
<% elsif field.type =~ /int64/ %>
|
433
|
+
{
|
434
|
+
uint64_t value = 0;
|
435
|
+
offset = read_raw_varint64(&value, buffer, offset);
|
436
|
+
pb-><%= field.name %> = (int64_t)tag;
|
437
|
+
}
|
438
|
+
<% elsif field.type =~ /sint32/ %>
|
439
|
+
offset = read_raw_varint32(&tag, buffer, offset);
|
440
|
+
pb-><%= field.name %> = decode_zig_zag32(tag);
|
441
|
+
<% elsif field.type =~ /sint64/ %>
|
442
|
+
{
|
443
|
+
uint64_t value = 0;
|
444
|
+
offset = read_raw_varint64(&value, buffer, offset);
|
445
|
+
pb-><%= field.name %> = decode_zig_zag64(value);
|
446
|
+
}
|
447
|
+
<% elsif field.type =~ /uint32/ %>
|
448
|
+
offset = read_raw_varint32(&tag, buffer, offset);
|
449
|
+
pb-><%= field.name %> = tag;
|
450
|
+
<% elsif field.type =~ /uint64/ %>
|
451
|
+
{
|
452
|
+
uint64_t value = 0;
|
453
|
+
offset = read_raw_varint64(&value, buffer, offset);
|
454
|
+
pb-><%= field.name %> = value;
|
455
|
+
}
|
456
|
+
<% elsif field.type =~ /fixed32/ %>
|
457
|
+
offset = read_raw_little_endian32(&tag, buffer, offset);
|
458
|
+
pb-><%= field.name %> = tag;
|
459
|
+
<% elsif field.type =~ /fixed64/ %>
|
460
|
+
{
|
461
|
+
uint64_t value = 0;
|
462
|
+
offset = read_raw_little_endian64(&value, buffer, offset);
|
463
|
+
pb-><%= field.name %> = value;
|
464
|
+
}
|
465
|
+
<% elsif field.type =~ /sfixed32/ %>
|
466
|
+
offset = read_raw_little_endian32(&tag, buffer, offset);
|
467
|
+
pb-><%= field.name %> = (int32_t)tag;
|
468
|
+
<% elsif field.type =~ /sfixed64/ %>
|
469
|
+
{
|
470
|
+
uint64_t value = 0;
|
471
|
+
offset = read_raw_little_endian64(&value, buffer, offset);
|
472
|
+
pb-><%= field.name %> = (int64_t)value;
|
473
|
+
}
|
474
|
+
<% elsif field.type =~ /bool/ %>
|
475
|
+
offset = read_raw_varint32(&tag, buffer, offset);
|
476
|
+
pb-><%= field.name %> = tag & 1;
|
477
|
+
<% elsif field.type =~ /string|bytes/ %>
|
478
|
+
offset = read_raw_varint32(&tag, buffer, offset);
|
479
|
+
pb-><%= field.name %>.size = (size_t)tag;
|
480
|
+
for (size_t i = 0; i < (size_t)tag; ++i)
|
481
|
+
offset = read_raw_byte((pb-><%= field.name %>.data + i), buffer, offset);
|
482
|
+
<% end %>
|
483
|
+
break;
|
484
|
+
<% end %>
|
485
|
+
}
|
486
|
+
}
|
487
|
+
return offset;
|
488
|
+
}
|
489
|
+
|
490
|
+
int <%= namespaced_function exp.name, 'read', 'delimited', 'from' %>(<%= namespaced_type exp.name %> *pb, const uint8_t *buffer, int offset) {
|
491
|
+
uint32_t size;
|
492
|
+
offset = read_raw_varint32(&size, buffer, offset);
|
493
|
+
<%= namespaced_function exp.name, 'read' %>(pb, buffer, offset, size + offset);
|
494
|
+
return offset + size;
|
495
|
+
}
|
496
|
+
|
497
|
+
int <%= namespaced_function exp.name, 'decode' %>(<%= namespaced_type exp.name %> *pb, const uint8_t *data, size_t data_size, size_t *encoded_size) {
|
498
|
+
*encoded_size = <%= namespaced_function exp.name, 'read' %>(pb, data, 0, data_size);
|
499
|
+
return 0; // TODO: Decoding errors.
|
500
|
+
}
|
501
|
+
<% end %>
|
502
|
+
<% end %>
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <stdbool.h>
|
4
|
+
#include <stddef.h>
|
5
|
+
#include <stdint.h>
|
6
|
+
|
7
|
+
#ifdef __cplusplus
|
8
|
+
extern "C" {
|
9
|
+
#endif
|
10
|
+
<%
|
11
|
+
each do |exp|
|
12
|
+
if exp.kind_of?(Protobuf::Generate::Ast::Package)
|
13
|
+
package exp.name
|
14
|
+
elsif exp.kind_of?(Protobuf::Generate::Ast::Enum)
|
15
|
+
%>
|
16
|
+
|
17
|
+
typedef enum {
|
18
|
+
<% exp.fields.each do |field| %>
|
19
|
+
<%= namespaced_constant exp.name, field.name %> = <%= field.tag %>,
|
20
|
+
<% end %>
|
21
|
+
} <%= namespaced_type exp.name %>;
|
22
|
+
<%
|
23
|
+
elsif exp.kind_of?(Protobuf::Generate::Ast::Message)
|
24
|
+
%>
|
25
|
+
|
26
|
+
typedef struct <%= namespaced_type exp.name %> {
|
27
|
+
<% exp.fields.each do |field| %>
|
28
|
+
<% if type_message?(field.type) %>
|
29
|
+
<%= namespaced_type field.type %> <%= field.name %>;
|
30
|
+
<% elsif type_enum?(field.type) %>
|
31
|
+
<%= namespaced_type field.type %> <%= field.name %>;
|
32
|
+
<% elsif field.type =~ /double/ %>
|
33
|
+
double <%= field.name %>;
|
34
|
+
<% elsif field.type =~ /float/ %>
|
35
|
+
float <%= field.name %>;
|
36
|
+
<% elsif field.type =~ /int32|sint32|sfixed32/ %>
|
37
|
+
int32_t <%= field.name %>;
|
38
|
+
<% elsif field.type =~ /int64|sint64|sfixed64/ %>
|
39
|
+
int64_t <%= field.name %>;
|
40
|
+
<% elsif field.type =~ /uint32|fixed32/ %>
|
41
|
+
uint32_t <%= field.name %>;
|
42
|
+
<% elsif field.type =~ /uint64|fixed64/ %>
|
43
|
+
uint64_t <%= field.name %>;
|
44
|
+
<% elsif field.type =~ /bool/ %>
|
45
|
+
bool <%= field.name %>;
|
46
|
+
<% elsif field.type =~ /string/ %>
|
47
|
+
struct {
|
48
|
+
uint8_t data[<%= field.meta["size_max"] %>];
|
49
|
+
size_t size;
|
50
|
+
} <%= field.name %>;
|
51
|
+
<% elsif field.type =~ /bytes/ %>
|
52
|
+
struct {
|
53
|
+
uint8_t data[<%= field.meta["size_max"] %>];
|
54
|
+
size_t size;
|
55
|
+
} <%= field.name %>;
|
56
|
+
<% end %>
|
57
|
+
<% end %>
|
58
|
+
} <%= namespaced_type exp.name %>;
|
59
|
+
|
60
|
+
// TODO: Replace int with an protobuf_(encode|decode)_t type.
|
61
|
+
// TODO: Move encode_size into result type.
|
62
|
+
int <%= namespaced_function exp.name, 'encode' %>(const <%= namespaced_type exp.name %> *pb, uint8_t *data, size_t data_size, size_t *encoded_size);
|
63
|
+
int <%= namespaced_function exp.name, 'decode' %>(<%= namespaced_type exp.name %> *pb, const uint8_t *data, size_t data_size, size_t *encoded_size);
|
64
|
+
<%
|
65
|
+
end
|
66
|
+
end
|
67
|
+
%>
|
68
|
+
|
69
|
+
#ifdef __cplusplus
|
70
|
+
}
|
71
|
+
#endif
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'protobuf/generate/language'
|
2
|
+
|
3
|
+
module Protobuf
|
4
|
+
module Generate
|
5
|
+
class Language
|
6
|
+
class C < Language
|
7
|
+
match %r{^ (?:gnu|[Cc]) (?:99|11) $}x
|
8
|
+
templates Dir.glob(File.join(File.expand_path(File.dirname(__FILE__)), 'c', '*.erb'))
|
9
|
+
|
10
|
+
module Conventions
|
11
|
+
def package name = nil
|
12
|
+
@naming_namespace = name.to_s if name
|
13
|
+
(@naming_namespace ||= '').gsub('.', '_')
|
14
|
+
end
|
15
|
+
|
16
|
+
def type *name; snake_case *name, 't' end
|
17
|
+
def variable *name; snake_case *name end
|
18
|
+
def constant *name; snake_case(*name).upcase end
|
19
|
+
def function *name; snake_case *name end
|
20
|
+
|
21
|
+
def namespaced_type *name; type package, *name end
|
22
|
+
def namespaced_variable *name; variable package, *name end
|
23
|
+
def namespaced_constant *name; constant package, *name end
|
24
|
+
def namespaced_function *name; function package, *name end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def snake_case *name
|
28
|
+
name.map(&:to_s).join('_').gsub(/([^A-Z_])([A-Z]+)/, '\1_\2').gsub(/_+/, '_').downcase
|
29
|
+
end
|
30
|
+
end # Conventions
|
31
|
+
|
32
|
+
def initialize ast, conventions = Conventions
|
33
|
+
ast.extend(conventions)
|
34
|
+
super ast
|
35
|
+
end
|
36
|
+
end # C
|
37
|
+
end # Langage
|
38
|
+
end # Generate
|
39
|
+
end # Protobuf
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'erubis'
|
2
|
+
|
3
|
+
module Protobuf
|
4
|
+
module Generate
|
5
|
+
class Language
|
6
|
+
module Helpers
|
7
|
+
attr_accessor :template, :filename
|
8
|
+
|
9
|
+
def type_message? type
|
10
|
+
!!find{|e| e.kind_of?(Protobuf::Generate::Ast::Message) && e.name == type }
|
11
|
+
end
|
12
|
+
|
13
|
+
def type_enum? type
|
14
|
+
!!find{|e| e.kind_of?(Protobuf::Generate::Ast::Enum) && e.name == type }
|
15
|
+
end
|
16
|
+
|
17
|
+
def type_wire type
|
18
|
+
Hash[*%w{int32 0 int64 0 sint32 0 sint64 0 uint32 0 uint64 0 string 2 bool 0 float 5 double 1 fixed32 5 fixed64 1 sfixed32 5 sfixed64 1 bytes 2}][type]
|
19
|
+
end
|
20
|
+
|
21
|
+
def type_enum_default type, default
|
22
|
+
enum = find{|e| e.kind_of?(Protobuf::Generate::Ast::Enum) && e.name == type} # TODO: Or raise unknown type.
|
23
|
+
(enum.fields.find{|f| f.name == default.to_s} || enum.fields.first).name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.match language = nil
|
28
|
+
@language = language if language
|
29
|
+
@language
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.templates templates = nil
|
33
|
+
@templates = templates if templates
|
34
|
+
@templates
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.find language
|
38
|
+
@@languages.find{|l| l.match(language.to_s)}
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.inherited klass
|
42
|
+
(@@languages ||= []) << klass
|
43
|
+
end
|
44
|
+
|
45
|
+
#--
|
46
|
+
# TODO: AST tree should include name of .proto that generated it.
|
47
|
+
def initialize ast
|
48
|
+
@ast = ast
|
49
|
+
end
|
50
|
+
|
51
|
+
def templates
|
52
|
+
self.class.templates
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate template, filename
|
56
|
+
ast = @ast
|
57
|
+
ast.extend(Helpers)
|
58
|
+
ast.template = template
|
59
|
+
ast.filename = filename
|
60
|
+
Erubis::Eruby.new(File.read(template), filename: template).evaluate(ast)
|
61
|
+
end
|
62
|
+
end # Language
|
63
|
+
end # Generate
|
64
|
+
end # Protobuf
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module Protobuf
|
4
|
+
module Generate
|
5
|
+
class Parser < Parslet::Parser
|
6
|
+
rule(:newline) { match('\n') }
|
7
|
+
rule(:whitespace) { match('\s').repeat(1) }
|
8
|
+
rule(:whitespace?) { whitespace.maybe }
|
9
|
+
rule(:space) { match('[\t ]').repeat(1) }
|
10
|
+
rule(:space?) { space.maybe }
|
11
|
+
|
12
|
+
rule(:comment_option) { str('@') >> identifier.as(:name) >> space? >> equals >> constant.as(:value) }
|
13
|
+
rule(:comment_option_list) { (comment_option.as(:option) >> space?).repeat(1) }
|
14
|
+
rule(:comment) { str('//') >> (newline.absent? >> (comment_option_list.as(:meta) | any)).repeat >> newline }
|
15
|
+
rule(:comment_list) { comment.repeat }
|
16
|
+
|
17
|
+
rule(:equals) { str('=') >> whitespace? }
|
18
|
+
rule(:bracket_open) { str('{') >> whitespace? }
|
19
|
+
rule(:bracket_close) { str('}') >> whitespace? }
|
20
|
+
|
21
|
+
rule(:digit) { match('[0-9]') }
|
22
|
+
rule(:integer) { str('-').maybe >> match('[1-9]') >> digit.repeat }
|
23
|
+
rule(:float) { str('-').maybe >> digit.repeat(1) >> str('.') >> digit.repeat(1) }
|
24
|
+
|
25
|
+
rule(:string_special) { match['\0\t\n\r"\\\\'] }
|
26
|
+
rule(:escaped_special) { str("\\") >> match['0tnr"\\\\'] }
|
27
|
+
rule(:string) { str('"') >> (escaped_special | string_special.absent? >> any).repeat >> str('"') }
|
28
|
+
|
29
|
+
rule(:identifier) { match('[a-zA-Z_]') >> match('[a-zA-Z0-9_]').repeat }
|
30
|
+
rule(:identifier_dot_list) { identifier >> (str('.') >> identifier).repeat }
|
31
|
+
|
32
|
+
rule(:constant) { identifier | integer | float | string }
|
33
|
+
|
34
|
+
rule(:field_option) { str('default').as(:name) >> whitespace? >> equals >> constant.as(:value) }
|
35
|
+
rule(:field_option_list) { (str('[') >> whitespace? >> (field_option.as(:option) >> whitespace?).repeat(1) >> whitespace? >> str(']') >> whitespace?) }
|
36
|
+
rule(:field_type) { identifier.as(:type) >> whitespace? }
|
37
|
+
rule(:field_label) { (str('required') | str('optional') | str('repeated')).as(:label) >> whitespace? }
|
38
|
+
|
39
|
+
rule(:message_field) { (field_label >> field_type >> identifier.as(:name) >> whitespace? >> equals >> integer.as(:tag) >> whitespace? >> field_option_list.maybe.as(:options) >> str(';') >> space? >> comment.maybe.as(:comment) ).as(:message_field) }
|
40
|
+
rule(:message_field_list) { (message_field >> whitespace?).repeat(1).as(:fields) }
|
41
|
+
rule(:message) { (comment_list.maybe.as(:comments) >> str('message') >> whitespace? >> identifier.as(:name) >> whitespace? >> bracket_open >> message_field_list.maybe >> bracket_close >> whitespace?).as(:message) }
|
42
|
+
|
43
|
+
rule(:enum_field) { (identifier.as(:name) >> whitespace? >> equals >> integer.as(:tag) >> whitespace? >> str(';')).as(:enum_field) }
|
44
|
+
rule(:enum_field_list) { (enum_field >> whitespace?).repeat(1).as(:fields) }
|
45
|
+
rule(:enum) { (str('enum') >> whitespace? >> identifier.as(:name) >> whitespace? >> bracket_open >> enum_field_list >> bracket_close >> whitespace?).as(:enum) }
|
46
|
+
|
47
|
+
rule(:package) { (str('package') >> whitespace? >> identifier_dot_list.as(:name) >> whitespace? >> str(';') >> whitespace?).as(:package) }
|
48
|
+
|
49
|
+
rule(:expression) { whitespace? >> (package | enum | message | comment | whitespace).repeat }
|
50
|
+
root(:expression)
|
51
|
+
end # Parser
|
52
|
+
|
53
|
+
class Ast
|
54
|
+
class Package < Struct.new(:name); end
|
55
|
+
class Message < Struct.new(:name, :meta, :fields)
|
56
|
+
def empty?; fields.nil? or fields.empty? end
|
57
|
+
end
|
58
|
+
class MessageField < Struct.new(:label, :type, :name, :tag, :meta, :options)
|
59
|
+
def required?; !!label.match(/required/) end
|
60
|
+
def optional?; !required? end
|
61
|
+
end
|
62
|
+
class Enum < Struct.new(:name, :fields); end
|
63
|
+
class EnumField < Struct.new(:name, :tag); end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Transform < Parslet::Transform
|
67
|
+
rule(option: {name: simple(:name), value: simple(:value)}){ [name.to_s, value.to_s] }
|
68
|
+
rule(package: {name: simple(:name)}){ Ast::Package.new(name.to_s) }
|
69
|
+
|
70
|
+
rule(message_field: subtree(:f)) do
|
71
|
+
Ast::MessageField.new(
|
72
|
+
*f.values_at(:label, :type, :name, :tag).map(&:to_s),
|
73
|
+
f[:comment].kind_of?(Array) ? Hash[*f[:comment].map{|c| c[:meta]}.flatten.compact] : {},
|
74
|
+
f[:options].kind_of?(Array) ? Hash[*[f[:options]].flatten.compact] : {}
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
rule(message: subtree(:message)) do
|
79
|
+
Ast::Message.new(
|
80
|
+
message[:name].to_s,
|
81
|
+
# TODO: Parslet should return nil not "" when empty. Figure out what's going on.
|
82
|
+
message[:comments].kind_of?(Array) ? Hash[*message[:comments].map{|c| c[:meta]}.flatten.compact] : {},
|
83
|
+
[*message[:fields]].compact
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
rule(enum_field: {name: simple(:name), tag: simple(:tag)}){ Ast::EnumField.new(name.to_s, tag.to_s) }
|
88
|
+
rule(enum: subtree(:enum)){ Ast::Enum.new(enum[:name].to_s, enum[:fields]) }
|
89
|
+
end # Transform
|
90
|
+
end # Generate
|
91
|
+
end # Protobuf
|
92
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
|
3
|
+
spec = Gem::Specification.new do |s|
|
4
|
+
s.name = 'protobuf-generate'
|
5
|
+
s.version = '0.0.1'
|
6
|
+
s.summary = 'A multi-language concrete protobuf code generator.'
|
7
|
+
s.description = 'A simple PEG parser, AST and template based approach to code generation.'
|
8
|
+
s.authors = ['Shane Hanna']
|
9
|
+
s.email = ['shane.hanna@gmail.com']
|
10
|
+
s.licenses = ['MIT']
|
11
|
+
s.homepage = 'https://bitbucket.org/shanehanna/protobuf-generate'
|
12
|
+
|
13
|
+
s.add_dependency('parslet')
|
14
|
+
s.add_dependency('erubis')
|
15
|
+
|
16
|
+
s.required_ruby_version = '>= 1.9.2'
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: protobuf-generate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shane Hanna
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parslet
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: erubis
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A simple PEG parser, AST and template based approach to code generation.
|
42
|
+
email:
|
43
|
+
- shane.hanna@gmail.com
|
44
|
+
executables:
|
45
|
+
- protobuf-generate
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- bin/protobuf-generate
|
54
|
+
- lib/protobuf-generate.rb
|
55
|
+
- lib/protobuf/generate/language.rb
|
56
|
+
- lib/protobuf/generate/language/c.rb
|
57
|
+
- lib/protobuf/generate/language/c/c.erb
|
58
|
+
- lib/protobuf/generate/language/c/h.erb
|
59
|
+
- lib/protobuf/generate/parser.rb
|
60
|
+
- protobuf-generate.gemspec
|
61
|
+
homepage: https://bitbucket.org/shanehanna/protobuf-generate
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.9.2
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.0.3
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: A multi-language concrete protobuf code generator.
|
85
|
+
test_files: []
|
86
|
+
has_rdoc:
|