msgpack-idl 0.1.0

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,499 @@
1
+ #
2
+ # MessagePack IDL Processor
3
+ #
4
+ # Copyright (C) 2011 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module MessagePack
19
+ module IDL
20
+
21
+
22
+ class ParsletParser < Parslet::Parser
23
+ class << self
24
+ def sequence(name, separator, element, min=0)
25
+ if min == 0
26
+ eval %[rule(:#{name.to_s.dump}) {
27
+ (#{element}.as(:sequence_x) >> (#{separator} >> #{element}.as(:sequence_xs)).repeat).maybe.as(:sequence)
28
+ }]
29
+ else
30
+ eval %[rule(:#{name.to_s.dump}) {
31
+ (#{element}.as(:sequence_x) >> (#{separator} >> #{element}.as(:sequence_xs)).repeat(#{min-1})).as(:sequence)
32
+ }]
33
+ end
34
+ end
35
+
36
+ def keyword(string, name="k_"+string)
37
+ rule(name.to_sym) {
38
+ space? >> str(string) >> boundary
39
+ }
40
+ end
41
+
42
+ def separator(char, name)
43
+ rule(name.to_sym) {
44
+ space? >> str(char)
45
+ }
46
+ end
47
+ end
48
+
49
+ root :expression
50
+
51
+ rule(:expression) {
52
+ space? >> document >> space?
53
+ }
54
+
55
+ rule(:document) {
56
+ (include_ | definition).repeat.as(:document)
57
+ }
58
+
59
+ rule(:include_) {
60
+ k_include >>
61
+ path.as(:include) >>
62
+ eol
63
+ }
64
+
65
+ rule(:definition) {
66
+ namespace |
67
+ message |
68
+ enum |
69
+ exception |
70
+ service |
71
+ application
72
+ #const |
73
+ #typedef |
74
+ #typespec |
75
+ }
76
+
77
+
78
+ rule(:namespace) {
79
+ k_namespace >> (
80
+ lang_name.as(:namespace_lang) >> namespace_name.as(:namespace_name) |
81
+ namespace_name.as(:namespace_name)
82
+ ) >> eol
83
+ }
84
+
85
+
86
+ rule(:message) {
87
+ k_message >>
88
+ class_name.as(:message_name) >>
89
+ lt_extend_class.maybe.as(:super_class) >>
90
+ k_lwing >>
91
+ field.repeat.as(:message_body) >>
92
+ k_rwing
93
+ }
94
+
95
+ rule(:exception) {
96
+ k_exception >>
97
+ class_name.as(:exception_name) >>
98
+ lt_extend_class.maybe.as(:super_class) >>
99
+ k_lwing >>
100
+ field.repeat.as(:exception_body) >>
101
+ k_rwing
102
+ }
103
+
104
+
105
+ rule(:field) {
106
+ field_element >> eol
107
+ }
108
+
109
+ rule(:field_element) {
110
+ field_id.as(:field_id) >>
111
+ field_modifier.maybe.as(:field_modifier) >>
112
+ field_type.as(:field_type) >>
113
+ field_name.as(:field_name) >>
114
+ eq_value.maybe.as(:field_value)
115
+ }
116
+
117
+ rule(:field_id) {
118
+ # terminal
119
+ space? >>
120
+ (str('0') | (match('[1-9]') >> match('[0-9]').repeat)).as(:val_int) >>
121
+ str(':') >> str(':').absent?
122
+ }
123
+
124
+ rule(:field_modifier) {
125
+ k_optional.as(:val_optional) |
126
+ k_required.as(:val_required)
127
+ }
128
+
129
+
130
+ rule(:enum) {
131
+ k_enum >>
132
+ class_name.as(:enum_name) >>
133
+ k_lwing >>
134
+ enum_field.repeat.as(:enum_body) >>
135
+ k_rwing
136
+ }
137
+
138
+ rule(:enum_field) {
139
+ enum_field_element >> eol
140
+ }
141
+
142
+ rule(:enum_field_element) {
143
+ field_id.as(:enum_field_id) >> field_name.as(:enum_field_name)
144
+ }
145
+
146
+
147
+ rule(:service) {
148
+ k_service >>
149
+ service_name.as(:service_name) >>
150
+ service_version.maybe.as(:service_version) >>
151
+ k_lwing >>
152
+ func.repeat.as(:service_funcs) >>
153
+ k_rwing
154
+ }
155
+
156
+ rule(:service_version) {
157
+ # terminal
158
+ space? >> str(':') >> space? >>
159
+ (str('0') | (match('[1-9]') >> match('[0-9]').repeat)).as(:val_int)
160
+ }
161
+
162
+ rule(:func) {
163
+ return_type.as(:return_type) >>
164
+ func_name.as(:func_name) >>
165
+ k_lparen >>
166
+ func_args.as(:func_args) >>
167
+ k_rparen >>
168
+ throws_classes.maybe.as(:func_throws) >>
169
+ eol
170
+ }
171
+
172
+ sequence :func_args_seq, :k_comma, :field_element
173
+
174
+ rule(:func_args) {
175
+ func_args_seq
176
+ }
177
+
178
+ sequence :throws_classes_seq, :k_comma, :generic_type, 1
179
+
180
+ rule(:throws_classes) {
181
+ k_throws >> throws_classes_seq
182
+ }
183
+
184
+
185
+ rule(:application) {
186
+ k_application >>
187
+ service_name.as(:application_name) >>
188
+ k_lwing >>
189
+ scope.repeat.as(:application_body) >>
190
+ k_rwing
191
+ }
192
+
193
+ rule(:scope) {
194
+ service_name.as(:scope_service) >>
195
+ service_version.as(:scope_service_version) >>
196
+ field_name.as(:scope_name) >>
197
+ k_default.maybe.as(:scope_default) >>
198
+ eol
199
+ }
200
+
201
+
202
+ rule(:eq_value) {
203
+ k_equal >> literal
204
+ }
205
+
206
+ rule(:lt_extend_class) {
207
+ k_lpoint >> generic_type
208
+ }
209
+
210
+
211
+ rule(:field_type) {
212
+ generic_type.as(:field_type) >> k_question.maybe.as(:field_type_maybe)
213
+ }
214
+
215
+ rule(:return_type) {
216
+ field_type
217
+ }
218
+
219
+ rule(:generic_type) {
220
+ class_name.as(:generic_type) >> type_param.maybe.as(:type_params)
221
+ }
222
+
223
+ sequence :type_param_seq, :k_comma, :generic_type, 1
224
+
225
+ rule(:type_param) {
226
+ k_lpoint >> type_param_seq >> k_rpoint
227
+ }
228
+
229
+ sequence :type_param_decl_seq, :k_comma, :class_name, 1
230
+
231
+ rule(:type_param_decl) {
232
+ k_lpoint >> type_param_decl_seq >> k_rpoint
233
+ }
234
+
235
+
236
+ rule(:literal) {
237
+ literal_nil | literal_bool | literal_int |
238
+ literal_enum |
239
+ #literal_float | literal_str |
240
+ #literal_list | literal_map |
241
+ literal_const
242
+ }
243
+
244
+ rule(:literal_nil) {
245
+ k_nil.as(:literal_nil)
246
+ }
247
+
248
+ rule(:literal_bool) {
249
+ k_true.as(:literal_true) | k_false.as(:literal_false)
250
+ }
251
+
252
+ rule(:literal_const) {
253
+ const_name.as(:literal_const)
254
+ }
255
+
256
+ rule(:literal_enum) {
257
+ class_name.as(:literal_enum_name) >> str('.') >> field_name.as(:literal_enum_field)
258
+ }
259
+
260
+ rule(:literal_int) {
261
+ space? >> (
262
+ (str('-') | str('+')).maybe >>
263
+ (str('0') | (match('[1-9]') >> match('[0-9]').repeat))
264
+ ).as(:literal_int) >>
265
+ boundary
266
+ }
267
+
268
+ rule(:literal_float) {
269
+ space? >> (
270
+ (str('0') | (match('[1-9]') >> match('[0-9]').repeat)) >> str('.') >> match('[0-9]').repeat(1)
271
+ ).as(:literal_float) >>
272
+ boundary
273
+ }
274
+
275
+ rule(:literal_str_dq) {
276
+ space? >> str('"') >> (
277
+ (str('\\') >> any | str('"').absent? >> any ).repeat
278
+ ).as(:literal_str_dq) >>
279
+ str('"')
280
+ }
281
+
282
+ rule(:literal_str_sq) {
283
+ space? >> str("'") >> (
284
+ (str('\\') >> any | str("'").absent? >> any ).repeat
285
+ ).as(:literal_str_sq) >>
286
+ str("'")
287
+ }
288
+
289
+ rule(:literal_str) {
290
+ (literal_str_dq | literal_str_sq).repeat(1).as(:literal_str_seq)
291
+ }
292
+
293
+ #sequence :literal_list_seq, :k_comma, :literal
294
+ #
295
+ #rule(:literal_list) {
296
+ # space? >> k_lbracket >>
297
+ # literal_list_seq.as(:literal_list) >>
298
+ # k_rbracket
299
+ #}
300
+
301
+ #sequence :literal_map_seq, :k_comma, :literal_map_pair
302
+ #
303
+ #rule(:literal_map) {
304
+ # space? >> k_lwing >>
305
+ # literal_map_seq.as(:literal_map) >>
306
+ # k_rwing
307
+ #}
308
+ #
309
+ #rule(:literal_map_pair) {
310
+ # literal.as(:literal_map_key) >> k_colon >> literal.as(:literal_map_value)
311
+ #}
312
+
313
+
314
+ rule(:path) {
315
+ # TODO path
316
+ space? >> match('[a-zA-Z0-9_\-\.\ ]').repeat(1).as(:path) >> boundary
317
+ }
318
+
319
+ rule(:lang_name) {
320
+ name
321
+ }
322
+
323
+ rule(:namespace_name) {
324
+ (name >> ((str('.') | str('::')) >> name).repeat).as(:sequence)
325
+ }
326
+
327
+ rule(:class_name) {
328
+ name
329
+ }
330
+
331
+ rule(:field_name) {
332
+ name
333
+ }
334
+
335
+ rule(:service_name) {
336
+ class_name
337
+ }
338
+
339
+ rule(:func_name) {
340
+ field_name
341
+ }
342
+
343
+ rule(:name) {
344
+ # terminal
345
+ space? >> (match('[a-zA-Z]') >> match('[a-zA-Z0-9_]').repeat).as(:name) >> boundary
346
+ }
347
+
348
+
349
+ rule(:inline_comment) {
350
+ str('/*') >> (
351
+ inline_comment | # accepts nested comments
352
+ (str('*') >> str('/').absent?) |
353
+ (str('*').absent? >> any)
354
+ ).repeat >> str('*/')
355
+ }
356
+
357
+ rule(:line_comment) {
358
+ (str('//') | str('#')) >>
359
+ (match('[\r\n]').absent? >> any).repeat >>
360
+ match('[\r\n]').repeat(1)
361
+ }
362
+
363
+ rule(:comment) {
364
+ inline_comment | line_comment
365
+ }
366
+
367
+
368
+ rule(:space) {
369
+ (match('[ \r\n\t]') | comment).repeat(1)
370
+ }
371
+
372
+ rule(:space?) {
373
+ space.maybe
374
+ }
375
+
376
+ rule(:eol) {
377
+ match('[ \t]').repeat >>
378
+ (match('[ \t;\r\n]') | line_comment).repeat(1)
379
+ }
380
+
381
+ rule(:boundary) {
382
+ match('[a-zA-Z0-9_]').absent?
383
+ }
384
+
385
+
386
+ keyword('include')
387
+ keyword('namespace')
388
+ keyword('message')
389
+ keyword('enum')
390
+ keyword('exception')
391
+ keyword('const')
392
+ keyword('typedef')
393
+ keyword('typespec')
394
+ keyword('service')
395
+ keyword('application')
396
+ keyword('optional')
397
+ keyword('required')
398
+ keyword('obsolete')
399
+ keyword('throws')
400
+ keyword('default')
401
+ keyword('nil')
402
+ keyword('true')
403
+ keyword('false')
404
+ keyword('void')
405
+
406
+ separator('=', :k_equal)
407
+ separator('{', :k_lwing)
408
+ separator('}', :k_rwing)
409
+ separator('(', :k_lparen)
410
+ separator(')', :k_rparen)
411
+ separator(':', :k_colon)
412
+ separator(',', :k_comma)
413
+ separator(';', :k_semi)
414
+ separator('<', :k_lpoint)
415
+ separator('>', :k_rpoint)
416
+ separator('[', :k_lbracket)
417
+ separator(']', :k_rbracket)
418
+ separator('-', :k_minus)
419
+ separator('+', :k_plus)
420
+ separator('!', :k_bang)
421
+ separator('?', :k_question)
422
+ separator('.', :k_dot)
423
+
424
+ LINE_HEAD_FORMAT = " % 4d: "
425
+ LINE_HEAD_SIZE = (LINE_HEAD_FORMAT % 0).size
426
+ AFTER_BUFFER = 200
427
+ AFTER_LINES = 3
428
+ BEFORE_BUFFER = 200
429
+ BEFORE_LINES = 4
430
+
431
+ def print_error(error, fname, out=STDERR)
432
+ error_tree = self.root.error_tree
433
+
434
+ last = error_tree
435
+ until last.children.empty?
436
+ last = last.children.last
437
+ end
438
+ last_cause = last.parslet.instance_eval('@last_cause') # FIXME
439
+ source = last_cause.source
440
+
441
+ row, col = source.line_and_column(last_cause.pos)
442
+
443
+ old_pos = source.pos
444
+ begin
445
+ source.pos = last_cause.pos - col + 1
446
+ line, *after = source.read(AFTER_BUFFER).to_s.split("\n")
447
+ after = after[0,AFTER_LINES]
448
+
449
+ source.pos = last_cause.pos - col - BEFORE_BUFFER
450
+ before = source.read(BEFORE_BUFFER).to_s.split("\n")
451
+ before = before[-BEFORE_LINES,BEFORE_LINES] || []
452
+ ensure
453
+ source.pos = old_pos
454
+ end
455
+
456
+ if m = /[ \t\r\n]*/.match(line)
457
+ heading = m[0]
458
+ else
459
+ heading = ""
460
+ end
461
+
462
+ out.puts "syntax error:"
463
+ (
464
+ error.to_s.split("\n") +
465
+ error_tree.to_s.split("\n")
466
+ ).each {|ln|
467
+ out.puts " "+ln
468
+ }
469
+
470
+ out.puts ""
471
+ out.puts "around line #{row} column #{heading.size}-#{col}:"
472
+ out.puts ""
473
+
474
+ before.each_with_index {|ln,i|
475
+ l = row - after.size - 1 + i
476
+ out.print LINE_HEAD_FORMAT % l
477
+ out.puts ln
478
+ }
479
+
480
+ out.print LINE_HEAD_FORMAT % row
481
+ out.puts line
482
+ out.print " "*LINE_HEAD_SIZE
483
+ out.puts heading + '^'*(col - heading.size)
484
+
485
+ after.each_with_index {|ln,i|
486
+ l = row + 1 + i
487
+ out.print LINE_HEAD_FORMAT % l
488
+ out.puts ln
489
+ }
490
+
491
+ out.puts ""
492
+
493
+ out
494
+ end
495
+ end
496
+
497
+
498
+ end
499
+ end