blob: f96125cc72c0601875c243a28a54135b13d05df0 [file] [log] [blame]
Zack Williams28f1e492019-02-01 10:02:56 -07001from __future__ import absolute_import
2from __future__ import print_function
Sapan Bhatiab1225872017-03-29 20:47:47 +02003
Zack Williams28f1e492019-02-01 10:02:56 -07004__author__ = "Dusan (Ph4r05) Klinec, Sapan Bhatia, ONF"
5__copyright__ = "Copyright (C) 2014 Dusan (ph4r05) Klinec, 2017-2019 ONF"
Dusan Klinecccaa0d92014-11-09 03:21:31 +01006__license__ = "Apache License, Version 2.0"
Dusan Klinecccaa0d92014-11-09 03:21:31 +01007
8import ply.lex as lex
9import ply.yacc as yacc
Dusan Klinecccaa0d92014-11-09 03:21:31 +010010
Zack Williamsbe7f36d2018-02-02 11:37:11 -070011from .model import (
12 DotName,
13 EnumDefinition,
14 EnumFieldDefinition,
15 ExtensionsDirective,
16 ExtensionsMax,
17 FieldDefinition,
18 FieldDirective,
19 FieldType,
20 ImportStatement,
21 LinkDefinition,
22 LinkSpec,
23 Literal,
24 MapDefinition,
25 MessageDefinition,
26 MessageExtension,
27 MethodDefinition,
28 Name,
29 OptionStatement,
30 PackageStatement,
31 PolicyDefinition,
32 ProtoFile,
33 ReduceDefinition,
34 ServiceDefinition,
35)
36
Zack Williams28f1e492019-02-01 10:02:56 -070037from .helpers import LexHelper, LU
38from .logicparser import FOLParser, FOLLexer, FOLParsingError
Sapan Bhatiaad79fee2017-06-26 23:35:57 -070039import ast
Scott Baker20997cf2019-04-04 10:10:06 -070040import sys
Sapan Bhatiaad79fee2017-06-26 23:35:57 -070041
Zack Williamsbe7f36d2018-02-02 11:37:11 -070042
Sapan Bhatiaad79fee2017-06-26 23:35:57 -070043class PythonError(Exception):
44 pass
Sapan Bhatiab1225872017-03-29 20:47:47 +020045
Zack Williamsbe7f36d2018-02-02 11:37:11 -070046
Sapan Bhatia9c579722018-01-12 13:45:09 -050047class ParsingError(Exception):
Zack Williamsbe7f36d2018-02-02 11:37:11 -070048
Sapan Bhatia9c579722018-01-12 13:45:09 -050049 def __init__(self, message, error_range):
50 super(ParsingError, self).__init__(message)
51 self.error_range = error_range
52
53
Dusan Klinecccaa0d92014-11-09 03:21:31 +010054class ProtobufLexer(object):
Zack Williamsbe7f36d2018-02-02 11:37:11 -070055 keywords = (
56 'double',
57 'float',
58 'int32',
59 'int64',
60 'uint32',
61 'uint64',
62 'sint32',
63 'sint64',
64 'fixed32',
65 'fixed64',
66 'sfixed32',
67 'sfixed64',
68 'bool',
69 'string',
70 'bytes',
71 'message',
72 'required',
73 'optional',
74 'repeated',
75 'enum',
76 'extensions',
77 'max',
78 'extend',
79 'to',
80 'package',
81 '_service',
82 'rpc',
83 'returns',
84 'true',
85 'false',
86 'option',
87 'import',
88 'manytoone',
89 'manytomany',
90 'onetoone',
91 'policy',
92 'map',
93 'reduce')
Dusan Klinecccaa0d92014-11-09 03:21:31 +010094
95 tokens = [
Sapan Bhatia64c72512017-06-23 02:32:45 -070096 'POLICYBODY',
Dusan Klinecccaa0d92014-11-09 03:21:31 +010097 'NAME',
98 'NUM',
99 'STRING_LITERAL',
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700100 # 'LINE_COMMENT', 'BLOCK_COMMENT',
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100101 'LBRACE', 'RBRACE', 'LBRACK', 'RBRACK',
Dusan Klinecc9b031a2014-11-10 13:21:08 +0100102 'LPAR', 'RPAR', 'EQ', 'SEMI', 'DOT',
Sapan Bhatia78fee772017-04-21 19:00:48 +0200103 'ARROW', 'COLON', 'COMMA', 'SLASH',
Sapan Bhatia64c72512017-06-23 02:32:45 -0700104 'DOUBLECOLON',
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100105 'STARTTOKEN'
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100106 ] + [k.upper() for k in keywords]
Sapan Bhatia64c72512017-06-23 02:32:45 -0700107
Sapan Bhatia9c579722018-01-12 13:45:09 -0500108 def t_POLICYBODY(self, t):
109 r'< (.|\n)*? [^-]>'
110 t.lexer.lineno += t.value.count('\n')
111 return t
Sapan Bhatia64c72512017-06-23 02:32:45 -0700112
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100113 literals = '()+-*/=?:,.^|&~!=[]{};<>@%'
114
Sapan Bhatia87792a12017-04-10 19:35:05 -0700115 t_NUM = r'[+-]?\d+(\.\d+)?'
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100116 t_STRING_LITERAL = r'\"([^\\\n]|(\\.))*?\"'
117
118 t_ignore_LINE_COMMENT = '//.*'
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700119
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100120 def t_BLOCK_COMMENT(self, t):
121 r'/\*(.|\n)*?\*/'
122 t.lexer.lineno += t.value.count('\n')
123
124 t_LBRACE = '{'
125 t_RBRACE = '}'
126 t_LBRACK = '\\['
127 t_RBRACK = '\\]'
Sapan Bhatia64c72512017-06-23 02:32:45 -0700128
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100129 t_LPAR = '\\('
130 t_RPAR = '\\)'
131 t_EQ = '='
132 t_SEMI = ';'
Sapan Bhatiab1225872017-03-29 20:47:47 +0200133 t_ARROW = '\\-\\>'
134 t_COLON = '\\:'
Sapan Bhatia78fee772017-04-21 19:00:48 +0200135 t_SLASH = '\\/'
Sapan Bhatiab1225872017-03-29 20:47:47 +0200136 t_COMMA = '\\,'
Dusan Klineca4fae112014-11-10 08:50:27 +0100137 t_DOT = '\\.'
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100138 t_ignore = ' \t\f'
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100139 t_STARTTOKEN = '\\+'
Sapan Bhatia64c72512017-06-23 02:32:45 -0700140 t_DOUBLECOLON = '\\:\\:'
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100141
142 def t_NAME(self, t):
Sapan Bhatia78fee772017-04-21 19:00:48 +0200143 '[A-Za-z_$][A-Za-z0-9_+$]*'
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100144 if t.value in ProtobufLexer.keywords:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700145 # print "type: %s val %s t %s" % (t.type, t.value, t)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100146 t.type = t.value.upper()
147 return t
148
149 def t_newline(self, t):
150 r'\n+'
151 t.lexer.lineno += len(t.value)
152
153 def t_newline2(self, t):
154 r'(\r\n)+'
155 t.lexer.lineno += len(t.value) / 2
156
157 def t_error(self, t):
Zack Williams28f1e492019-02-01 10:02:56 -0700158 print(("Illegal character '{}' ({}) in line {}".format(
Scott Baker20997cf2019-04-04 10:10:06 -0700159 t.value[0], hex(ord(t.value[0])), t.lexer.lineno)),
160 file=sys.stderr)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100161 t.lexer.skip(1)
162
Dusan Klinecc9b031a2014-11-10 13:21:08 +0100163
Sapan Bhatiab1225872017-03-29 20:47:47 +0200164def srcPort(x):
165 if (x):
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700166 return [FieldDirective(Name('port'), x)]
Sapan Bhatiab1225872017-03-29 20:47:47 +0200167 else:
168 return []
169
170
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100171class ProtobufParser(object):
172 tokens = ProtobufLexer.tokens
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100173 offset = 0
174 lh = LexHelper()
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700175 fol_lexer = lex.lex(module=FOLLexer()) # , optimize=1)
176 fol_parser = yacc.yacc(
177 module=FOLParser(),
178 start='goal',
179 outputdir='/tmp',
180 debug=0)
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100181
182 def setOffset(self, of):
183 self.offset = of
184 self.lh.offset = of
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100185
186 def p_empty(self, p):
187 '''empty :'''
188 pass
189
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700190 def p_field_modifier(self, p):
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100191 '''field_modifier : REQUIRED
192 | OPTIONAL
193 | REPEATED'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700194 p[0] = LU.i(p, 1)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100195
196 def p_primitive_type(self, p):
197 '''primitive_type : DOUBLE
198 | FLOAT
199 | INT32
200 | INT64
201 | UINT32
202 | UINT64
203 | SINT32
204 | SINT64
205 | FIXED32
206 | FIXED64
207 | SFIXED32
208 | SFIXED64
209 | BOOL
210 | STRING
211 | BYTES'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700212 p[0] = LU.i(p, 1)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100213
Sapan Bhatiab1225872017-03-29 20:47:47 +0200214 def p_link_type(self, p):
215 '''link_type : ONETOONE
216 | MANYTOONE
217 | MANYTOMANY'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700218 p[0] = LU.i(p, 1)
Sapan Bhatiab1225872017-03-29 20:47:47 +0200219
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100220 def p_field_id(self, p):
221 '''field_id : NUM'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700222 p[0] = LU.i(p, 1)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100223
Scott Bakerba36b492018-09-28 17:18:41 -0700224 def p_reverse_id(self, p):
225 '''reverse_id : NUM'''
226 p[0] = LU.i(p, 1)
227
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100228 def p_rvalue(self, p):
229 '''rvalue : NUM
230 | TRUE
231 | FALSE'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700232 p[0] = LU.i(p, 1)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100233
Sapan Bhatiab1225872017-03-29 20:47:47 +0200234 def p_rvalue3(self, p):
235 '''rvalue : STRING_LITERAL'''
236 p[0] = Name(LU.i(p, 1))
237 self.lh.set_parse_object(p[0], p)
238 p[0].deriveLex()
239
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100240 def p_rvalue2(self, p):
241 '''rvalue : NAME'''
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100242 p[0] = Name(LU.i(p, 1))
243 self.lh.set_parse_object(p[0], p)
244 p[0].deriveLex()
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100245
Sapan Bhatiab1225872017-03-29 20:47:47 +0200246 def p_field_directives2(self, p):
247 '''field_directives : empty'''
248 p[0] = []
249
250 def p_field_directives(self, p):
251 '''field_directives : LBRACK field_directive_times RBRACK'''
252 p[0] = p[2]
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700253 # self.lh.set_parse_object(p[0], p)
Sapan Bhatiab1225872017-03-29 20:47:47 +0200254
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100255 def p_field_directive(self, p):
Sapan Bhatiab1225872017-03-29 20:47:47 +0200256 '''field_directive : NAME EQ rvalue'''
257 p[0] = FieldDirective(Name(LU.i(p, 1)), LU.i(p, 3))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100258 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100259
Sapan Bhatiaa3686022017-06-24 07:24:19 -0700260 def p_policy_opt_explicit(self, p):
261 '''policy_opt : DOUBLECOLON NAME'''
262 p[0] = p[2]
263
Sapan Bhatiaa3686022017-06-24 07:24:19 -0700264 def p_policy_opt_empty(self, p):
265 '''policy_opt : empty'''
266 p[0] = None
Sapan Bhatia87792a12017-04-10 19:35:05 -0700267
268 def p_csv_expr(self, p):
269 '''csv_expr : LPAR csv RPAR'''
270 p[0] = p[2]
271
272 def p_csv_expr2(self, p):
273 '''csv_expr : empty'''
274 p[0] = []
275
276 def p_csv2(self, p):
277 '''csv : empty'''
278
279 def p_csv(self, p):
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700280 '''csv : dotname
Sapan Bhatia2ddf83a2017-06-10 04:31:40 -0700281 | csv COMMA dotname'''
Sapan Bhatia87792a12017-04-10 19:35:05 -0700282
283 if len(p) == 2:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700284 p[0] = [LU(p, 1)]
Sapan Bhatia87792a12017-04-10 19:35:05 -0700285 else:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700286 p[0] = p[1] + [LU(p, 3)]
Sapan Bhatia87792a12017-04-10 19:35:05 -0700287
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100288 def p_field_directive_times(self, p):
289 '''field_directive_times : field_directive_plus'''
290 p[0] = p[1]
291
292 def p_field_directive_times2(self, p):
293 '''field_directive_times : empty'''
294 p[0] = []
295
296 def p_field_directive_plus(self, p):
297 '''field_directive_plus : field_directive
Sapan Bhatiab1225872017-03-29 20:47:47 +0200298 | field_directive_plus COMMA field_directive'''
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100299 if len(p) == 2:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700300 p[0] = [LU(p, 1)]
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100301 else:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700302 p[0] = p[1] + [LU(p, 3)]
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100303
Dusan Klineca4fae112014-11-10 08:50:27 +0100304 def p_dotname(self, p):
305 '''dotname : NAME
306 | dotname DOT NAME'''
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100307 if len(p) == 2:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700308 p[0] = [LU(p, 1)]
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100309 else:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700310 p[0] = p[1] + [LU(p, 3)]
Dusan Klineca4fae112014-11-10 08:50:27 +0100311
312 # Hack for cases when there is a field named 'message' or 'max'
313 def p_fieldName(self, p):
Sapan Bhatia78fee772017-04-21 19:00:48 +0200314 '''field_name : STARTTOKEN
315 | NAME
Dusan Klineca4fae112014-11-10 08:50:27 +0100316 | MESSAGE
317 | MAX'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700318 p[0] = Name(LU.i(p, 1))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100319 self.lh.set_parse_object(p[0], p)
320 p[0].deriveLex()
Dusan Klineca4fae112014-11-10 08:50:27 +0100321
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100322 def p_field_type(self, p):
323 '''field_type : primitive_type'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700324 p[0] = FieldType(LU.i(p, 1))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100325 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100326
327 def p_field_type2(self, p):
Dusan Klineca4fae112014-11-10 08:50:27 +0100328 '''field_type : dotname'''
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100329 p[0] = DotName(LU.i(p, 1))
330 self.lh.set_parse_object(p[0], p)
331 p[0].deriveLex()
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100332
Sapan Bhatia78fee772017-04-21 19:00:48 +0200333 def p_slash_name(self, p):
Sapan Bhatia2ddf83a2017-06-10 04:31:40 -0700334 '''slash_name : SLASH dotname'''
Sapan Bhatia78fee772017-04-21 19:00:48 +0200335 p[0] = p[2]
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700336 # self.lh.set_parse_object(p[0], p)
Sapan Bhatia78fee772017-04-21 19:00:48 +0200337
338 def p_slash_name2(self, p):
339 '''slash_name : empty'''
340 p[0] = None
341
Sapan Bhatiab1225872017-03-29 20:47:47 +0200342 def p_colon_fieldname(self, p):
343 '''colon_fieldname : COLON field_name'''
344 p[0] = p[2]
345 self.lh.set_parse_object(p[0], p)
346
347 def p_colon_fieldname2(self, p):
348 '''colon_fieldname : empty'''
349 p[0] = None
350
351 # TODO: Add directives to link definition
352 def p_link_definition(self, p):
Zack Williams28f1e492019-02-01 10:02:56 -0700353 '''
354 link_definition : field_modifier link_type field_name policy_opt ARROW dotname slash_name colon_fieldname EQ field_id field_directives SEMI
355 '''
356
Sapan Bhatiab1225872017-03-29 20:47:47 +0200357 p[0] = LinkSpec(
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700358 FieldDefinition(
359 LU.i(
360 p, 1), Name('int32'), LU.i(
361 p, 3), LU.i(
362 p, 4), LU.i(
363 p, 10), [
364 FieldDirective(
365 Name('type'), Name('link')), FieldDirective(
366 Name('model'), LU.i(
367 p, 6))] + srcPort(
368 LU.i(
369 p, 8)) + LU.i(
370 p, 11)), LinkDefinition(
371 LU.i(
372 p, 2), LU.i(
373 p, 3), LU.i(
374 p, 6), LU.i(
375 p, 7), LU.i(
376 p, 8)))
Sapan Bhatiab1225872017-03-29 20:47:47 +0200377
378 self.lh.set_parse_object(p[0], p)
379
Scott Bakerba36b492018-09-28 17:18:41 -0700380 # TODO: Add directives to link definition
Zack Williams28f1e492019-02-01 10:02:56 -0700381 def p_link_definition_with_reverse(self, p):
382 '''
383 link_definition_with_reverse : field_modifier link_type field_name policy_opt ARROW dotname slash_name colon_fieldname EQ field_id COLON reverse_id field_directives SEMI
384 '''
Scott Bakerba36b492018-09-28 17:18:41 -0700385 p[0] = LinkSpec(
386 FieldDefinition(
387 LU.i(
388 p, 1), Name('int32'), LU.i(
389 p, 3), LU.i(
390 p, 4), LU.i(
391 p, 10), [
392 FieldDirective(
393 Name('type'), Name('link')), FieldDirective(
394 Name('model'), LU.i(
395 p, 6))] + srcPort(
396 LU.i(
397 p, 8)) + LU.i(
398 p, 13)), LinkDefinition(
399 LU.i(
400 p, 2), LU.i(
401 p, 3), LU.i(
402 p, 6), LU.i(
403 p, 7), LU.i(
Zack Williams28f1e492019-02-01 10:02:56 -0700404 p, 8), reverse_id=LU.i(p, 12)))
Scott Bakerba36b492018-09-28 17:18:41 -0700405
406 self.lh.set_parse_object(p[0], p)
407
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100408 # Root of the field declaration.
409 def p_field_definition(self, p):
Sapan Bhatiaa3686022017-06-24 07:24:19 -0700410 '''field_definition : field_modifier field_type field_name policy_opt EQ field_id field_directives SEMI'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700411 p[0] = FieldDefinition(
412 LU.i(
413 p, 1), LU.i(
414 p, 2), LU.i(
415 p, 3), LU.i(
416 p, 4), LU.i(
417 p, 6), LU.i(
418 p, 7))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100419 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100420
421 # Root of the enum field declaration.
422 def p_enum_field(self, p):
Dusan Klineca4fae112014-11-10 08:50:27 +0100423 '''enum_field : field_name EQ NUM SEMI'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700424 p[0] = EnumFieldDefinition(LU.i(p, 1), LU.i(p, 3))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100425 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100426
427 def p_enum_body_part(self, p):
428 '''enum_body_part : enum_field
429 | option_directive'''
430 p[0] = p[1]
431
432 def p_enum_body(self, p):
433 '''enum_body : enum_body_part
434 | enum_body enum_body_part'''
435 if len(p) == 2:
436 p[0] = [p[1]]
437 else:
438 p[0] = p[1] + [p[2]]
439
440 def p_enum_body_opt(self, p):
441 '''enum_body_opt : empty'''
442 p[0] = []
443
444 def p_enum_body_opt2(self, p):
445 '''enum_body_opt : enum_body'''
446 p[0] = p[1]
447
Sapan Bhatiaad79fee2017-06-26 23:35:57 -0700448 def p_reduce_definition(self, p):
449 '''reduce_definition : REDUCE NAME POLICYBODY'''
450 ltxt = p[3].lstrip('<').rstrip('>')
Zack Williams28f1e492019-02-01 10:02:56 -0700451 al = ast.parse(ltxt).body[0]
452 if not isinstance(al, ast.Expr):
Sapan Bhatiaad79fee2017-06-26 23:35:57 -0700453 raise PythonError("reduce operator needs to be an expression")
Zack Williams28f1e492019-02-01 10:02:56 -0700454 elif not isinstance(al.value, ast.Lambda):
Sapan Bhatiaad79fee2017-06-26 23:35:57 -0700455 raise PythonError("reduce operator needs to be a lambda")
456
457 p[0] = ReduceDefinition(Name(LU.i(p, 2)), ltxt)
458 self.lh.set_parse_object(p[0], p)
459
460 def p_map_definition(self, p):
461 '''map_definition : MAP NAME POLICYBODY'''
462 ltxt = p[3].lstrip('<').rstrip('>')
Zack Williams28f1e492019-02-01 10:02:56 -0700463 al = ast.parse(ltxt).body[0]
464 if not isinstance(al, ast.Expr):
Sapan Bhatiaad79fee2017-06-26 23:35:57 -0700465 raise PythonError("map operator needs to be an expression")
Zack Williams28f1e492019-02-01 10:02:56 -0700466 elif not isinstance(al.value, ast.Lambda):
Sapan Bhatiaad79fee2017-06-26 23:35:57 -0700467 raise PythonError("map operator needs to be a lambda")
468
469 p[0] = MapDefinition(Name(LU.i(p, 2)), ltxt)
470 self.lh.set_parse_object(p[0], p)
471
Sapan Bhatia64c72512017-06-23 02:32:45 -0700472 def p_policy_definition(self, p):
473 '''policy_definition : POLICY NAME POLICYBODY'''
Sapan Bhatia9c579722018-01-12 13:45:09 -0500474 try:
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700475 fol = self.fol_parser.parse(p[3], lexer=self.fol_lexer)
476 except FOLParsingError as e:
Sapan Bhatia9c579722018-01-12 13:45:09 -0500477 lineno, lexpos, length = e.error_range
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700478 raise ParsingError(
479 "Policy parsing error in policy %s" %
480 p[2], (p.lineno(3) + lineno, lexpos + p.lexpos(3), length))
Sapan Bhatia64c72512017-06-23 02:32:45 -0700481 p[0] = PolicyDefinition(Name(LU.i(p, 2)), fol)
482 self.lh.set_parse_object(p[0], p)
483
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100484 # Root of the enum declaration.
485 # enum_definition ::= 'enum' ident '{' { ident '=' integer ';' }* '}'
486 def p_enum_definition(self, p):
487 '''enum_definition : ENUM NAME LBRACE enum_body_opt RBRACE'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700488 p[0] = EnumDefinition(Name(LU.i(p, 2)), LU.i(p, 4))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100489 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100490
491 def p_extensions_to(self, p):
492 '''extensions_to : MAX'''
493 p[0] = ExtensionsMax()
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100494 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100495
496 def p_extensions_to2(self, p):
497 '''extensions_to : NUM'''
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100498 p[0] = LU.i(p, 1)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100499
500 # extensions_definition ::= 'extensions' integer 'to' integer ';'
501 def p_extensions_definition(self, p):
502 '''extensions_definition : EXTENSIONS NUM TO extensions_to SEMI'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700503 p[0] = ExtensionsDirective(LU.i(p, 2), LU.i(p, 4))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100504 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100505
506 # message_extension ::= 'extend' ident '{' message_body '}'
507 def p_message_extension(self, p):
508 '''message_extension : EXTEND NAME LBRACE message_body RBRACE'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700509 p[0] = MessageExtension(Name(LU.i(p, 2)), LU.i(p, 4))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100510 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100511
512 def p_message_body_part(self, p):
513 '''message_body_part : field_definition
Sapan Bhatiab1225872017-03-29 20:47:47 +0200514 | link_definition
Scott Bakerba36b492018-09-28 17:18:41 -0700515 | link_definition_with_reverse
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100516 | enum_definition
Sapan Bhatia4a159ac2017-04-29 20:10:05 +0200517 | option_directive
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100518 | message_definition
519 | extensions_definition
520 | message_extension'''
521 p[0] = p[1]
522
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700523 # message_body ::= { field_definition | enum_definition |
524 # message_definition | extensions_definition | message_extension }*
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100525 def p_message_body(self, p):
526 '''message_body : empty'''
527 p[0] = []
528
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700529 # message_body ::= { field_definition | enum_definition |
530 # message_definition | extensions_definition | message_extension }*
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100531 def p_message_body2(self, p):
532 '''message_body : message_body_part
533 | message_body message_body_part'''
534 if len(p) == 2:
535 p[0] = [p[1]]
536 else:
537 p[0] = p[1] + [p[2]]
538
539 # Root of the message declaration.
540 # message_definition = MESSAGE_ - ident("messageId") + LBRACE + message_body("body") + RBRACE
541 def p_message_definition(self, p):
Sapan Bhatiaa3686022017-06-24 07:24:19 -0700542 '''message_definition : MESSAGE NAME policy_opt csv_expr LBRACE message_body RBRACE'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700543 p[0] = MessageDefinition(
544 Name(
545 LU.i(
546 p, 2)), LU.i(
547 p, 3), LU.i(
548 p, 4), LU.i(
549 p, 6))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100550 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100551
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700552 # method_definition ::= 'rpc' ident '(' [ ident ] ')' 'returns' '(' [
553 # ident ] ')' ';'
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100554 def p_method_definition(self, p):
555 '''method_definition : RPC NAME LPAR NAME RPAR RETURNS LPAR NAME RPAR'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700556 p[0] = MethodDefinition(
557 Name(
558 LU.i(
559 p, 2)), Name(
560 LU.i(
561 p, 4)), Name(
562 LU.i(
563 p, 8)))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100564 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100565
566 def p_method_definition_opt(self, p):
567 '''method_definition_opt : empty'''
568 p[0] = []
569
570 def p_method_definition_opt2(self, p):
571 '''method_definition_opt : method_definition
572 | method_definition_opt method_definition'''
573 if len(p) == 2:
574 p[0] = [p[1]]
575 else:
576 p[0] = p[1] + [p[2]]
577
578 # service_definition ::= 'service' ident '{' method_definition* '}'
579 # service_definition = SERVICE_ - ident("serviceName") + LBRACE + ZeroOrMore(Group(method_definition)) + RBRACE
580 def p_service_definition(self, p):
Sapan Bhatiab1225872017-03-29 20:47:47 +0200581 '''service_definition : _SERVICE NAME LBRACE method_definition_opt RBRACE'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700582 p[0] = ServiceDefinition(Name(LU.i(p, 2)), LU.i(p, 4))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100583 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100584
585 # package_directive ::= 'package' ident [ '.' ident]* ';'
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700586 def p_package_directive(self, p):
Dusan Klineca4fae112014-11-10 08:50:27 +0100587 '''package_directive : PACKAGE dotname SEMI'''
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100588 p[0] = PackageStatement(Name(LU.i(p, 2)))
589 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100590
591 # import_directive = IMPORT_ - quotedString("importFileSpec") + SEMI
592 def p_import_directive(self, p):
593 '''import_directive : IMPORT STRING_LITERAL SEMI'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700594 p[0] = ImportStatement(Literal(LU.i(p, 2)))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100595 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100596
597 def p_option_rvalue(self, p):
598 '''option_rvalue : NUM
599 | TRUE
600 | FALSE'''
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100601 p[0] = LU(p, 1)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100602
603 def p_option_rvalue2(self, p):
604 '''option_rvalue : STRING_LITERAL'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700605 p[0] = Literal(LU(p, 1))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100606
607 def p_option_rvalue3(self, p):
608 '''option_rvalue : NAME'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700609 p[0] = Name(LU.i(p, 1))
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100610
611 # option_directive = OPTION_ - ident("optionName") + EQ + quotedString("optionValue") + SEMI
612 def p_option_directive(self, p):
613 '''option_directive : OPTION NAME EQ option_rvalue SEMI'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700614 p[0] = OptionStatement(Name(LU.i(p, 2)), LU.i(p, 4))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100615 self.lh.set_parse_object(p[0], p)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100616
Zack Williams28f1e492019-02-01 10:02:56 -0700617 # topLevelStatement = Group(message_definition | message_extension | enum_definition | service_definition |
618 # import_directive | option_directive | package_definition)
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700619 def p_topLevel(self, p):
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100620 '''topLevel : message_definition
621 | message_extension
622 | enum_definition
Sapan Bhatia64c72512017-06-23 02:32:45 -0700623 | policy_definition
Sapan Bhatia89bbaa52017-06-28 22:58:15 -0700624 | map_definition
625 | reduce_definition
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100626 | service_definition
627 | import_directive
Sapan Bhatiaff86b012017-06-11 14:44:15 -0700628 | package_directive
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100629 | option_directive'''
630 p[0] = p[1]
631
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100632 def p_statements2(self, p):
633 '''statements : topLevel
634 | statements topLevel'''
635 if len(p) == 2:
636 p[0] = [p[1]]
637 else:
638 p[0] = p[1] + [p[2]]
639
640 def p_statements(self, p):
641 '''statements : empty'''
642 p[0] = []
643
644 # parser = Optional(package_directive) + ZeroOrMore(topLevelStatement)
Dusan Klinecc9b031a2014-11-10 13:21:08 +0100645 def p_protofile(self, p):
Sapan Bhatiaff86b012017-06-11 14:44:15 -0700646 '''protofile : statements'''
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700647 p[0] = ProtoFile(LU.i(p, 1))
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100648 self.lh.set_parse_object(p[0], p)
Dusan Klinecc9b031a2014-11-10 13:21:08 +0100649
650 # Parsing starting point
651 def p_goal(self, p):
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100652 '''goal : STARTTOKEN protofile'''
Dusan Klinecc9b031a2014-11-10 13:21:08 +0100653 p[0] = p[2]
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100654
655 def p_error(self, p):
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700656 raise ParsingError("Parsing Error", (p.lineno, p.lexpos, len(p.value)))
657
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100658
659class ProtobufAnalyzer(object):
660
661 def __init__(self):
Sapan Bhatia9c579722018-01-12 13:45:09 -0500662 self.lexer = lex.lex(module=ProtobufLexer())
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700663 self.parser = yacc.yacc(
664 module=ProtobufParser(),
665 start='goal',
666 debug=0,
667 outputdir='/tmp')
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100668
669 def tokenize_string(self, code):
670 self.lexer.input(code)
671 for token in self.lexer:
672 print(token)
673
674 def tokenize_file(self, _file):
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700675 if isinstance(_file, str):
Zack Williams28f1e492019-02-01 10:02:56 -0700676 _file = open(_file)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100677 content = ''
678 for line in _file:
679 content += line
680 return self.tokenize_string(content)
681
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100682 def parse_string(self, code, debug=0, lineno=1, prefix='+'):
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100683 self.lexer.lineno = lineno
Dusan Klinecaa9ff472014-11-10 18:02:03 +0100684 self.parser.offset = len(prefix)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100685 return self.parser.parse(prefix + code, lexer=self.lexer, debug=debug)
686
687 def parse_file(self, _file, debug=0):
Zack Williamsbe7f36d2018-02-02 11:37:11 -0700688 if isinstance(_file, str):
Zack Williams28f1e492019-02-01 10:02:56 -0700689 _file = open(_file)
Dusan Klinecccaa0d92014-11-09 03:21:31 +0100690 content = ''
691 for line in _file:
692 content += line
693 return self.parse_string(content, debug=debug)