22
33from collections import OrderedDict
44import logging
5+ import six
56
67import ply .yacc as yacc
78
89from babelapi .babel .lexer import BabelLexer , BabelNull
910
1011class _Element (object ):
12+
1113 def __init__ (self , lineno , lexpos ):
14+ """
15+ Args:
16+ lineno (int): The line number where the start of this element
17+ occurs.
18+ lexpos (int): The character offset into the file where this element
19+ occurs.
20+ """
1221 self .lineno = lineno
1322 self .lexpos = lexpos
1423
1524class BabelNamespace (_Element ):
25+
1626 def __init__ (self , lineno , lexpos , name ):
1727 """
1828 Args:
@@ -28,6 +38,7 @@ def __repr__(self):
2838 return 'BabelNamespace({!r})' .format (self .name )
2939
3040class BabelInclude (_Element ):
41+
3142 def __init__ (self , lineno , lexpos , target ):
3243 """
3344 Args:
@@ -43,6 +54,7 @@ def __repr__(self):
4354 return 'BabelInclude({!r})' .format (self .target )
4455
4556class BabelAlias (_Element ):
57+
4658 def __init__ (self , lineno , lexpos , name , type_ref ):
4759 """
4860 Args:
@@ -57,36 +69,78 @@ def __repr__(self):
5769 return 'BabelAlias({!r}, {!r})' .format (self .name , self .type_ref )
5870
5971class BabelTypeDef (_Element ):
60- def __init__ (self , lineno , lexpos , composite_type , name , extends = None ,
61- coverage = None ):
72+
73+ def __init__ (self , lineno , lexpos , name , extends , doc , fields ):
74+ """
75+ Args:
76+ name (str): Name assigned to the type.
77+ extends (Optional[str]); Name of the type this inherits from.
78+ doc (Optional[str]): Docstring for the type.
79+ fields (List[BabelField]): Fields of a type, not including
80+ inherited ones.
81+ """
82+
6283 super (BabelTypeDef , self ).__init__ (lineno , lexpos )
63- self .composite_type = composite_type
84+
85+ assert isinstance (name , six .text_type ), type (name )
6486 self .name = name
87+ assert isinstance (extends , (six .text_type , type (None ))), type (extends )
6588 self .extends = extends
66- self . doc = None
67- self .fields = []
89+ assert isinstance ( doc , ( six . text_type , type ( None )))
90+ self .doc = doc
6891 self .examples = OrderedDict ()
69- self .coverage = coverage
70-
71- def set_doc (self , docstring ):
72- self .doc = docstring
73-
74- def set_fields (self , fields ):
92+ assert isinstance (fields , list )
7593 self .fields = fields
7694
7795 def add_example (self , label , text , example ):
7896 self .examples [label ] = (text , example )
7997
8098 def __str__ (self ):
8199 return self .__repr__ ()
100+
82101 def __repr__ (self ):
83- return 'BabelType({!r}, {!r}, {!r})' .format (
84- self .composite_type ,
102+ return 'BabelTypeDef({!r}, {!r}, {!r})' .format (
85103 self .name ,
104+ self .extends ,
105+ self .fields ,
106+ )
107+
108+ class BabelStructDef (BabelTypeDef ):
109+
110+ def __init__ (self , lineno , lexpos , name , extends , doc , fields ,
111+ subtypes = None ):
112+ """
113+ Args:
114+ subtypes (Tuple[List[BabelSubtypeField], bool]): Inner list
115+ enumerates subtypes. The bool indicates whether this struct
116+ is a catch-all.
117+
118+ See BabelTypeDef for other constructor args.
119+ """
120+
121+ super (BabelStructDef , self ).__init__ (
122+ lineno , lexpos , name , extends , doc , fields )
123+ assert isinstance (subtypes , (tuple , type (None ))), type (subtypes )
124+ self .subtypes = subtypes
125+
126+ def __repr__ (self ):
127+ return 'BabelStructDef({!r}, {!r}, {!r})' .format (
128+ self .name ,
129+ self .extends ,
130+ self .fields ,
131+ )
132+
133+ class BabelUnionDef (BabelTypeDef ):
134+
135+ def __repr__ (self ):
136+ return 'BabelUnionDef({!r}, {!r}, {!r})' .format (
137+ self .name ,
138+ self .extends ,
86139 self .fields ,
87140 )
88141
89142class BabelTypeRef (_Element ):
143+
90144 def __init__ (self , lineno , lexpos , name , args , nullable ):
91145 """
92146 Args:
@@ -107,6 +161,7 @@ def __repr__(self):
107161 )
108162
109163class BabelTagRef (_Element ):
164+
110165 def __init__ (self , lineno , lexpos , tag , union_name = None ):
111166 """
112167 Args:
@@ -128,6 +183,7 @@ class BabelField(_Element):
128183 Represents both a field of a struct and a field of a union.
129184 TODO(kelkabany): Split this into two different classes.
130185 """
186+
131187 def __init__ (self , lineno , lexpos , name , type_ref , deprecated ):
132188 """
133189 Args:
@@ -157,6 +213,7 @@ def __repr__(self):
157213 )
158214
159215class BabelVoidField (_Element ):
216+
160217 def __init__ (self , lineno , lexpos , name , catch_all ):
161218 super (BabelVoidField , self ).__init__ (lineno , lexpos )
162219 self .name = name
@@ -172,7 +229,21 @@ def __repr__(self):
172229 self .catch_all ,
173230 )
174231
232+ class BabelSubtypeField (_Element ):
233+
234+ def __init__ (self , lineno , lexpos , name , type_ref ):
235+ super (BabelSubtypeField , self ).__init__ (lineno , lexpos )
236+ self .name = name
237+ self .type_ref = type_ref
238+
239+ def __repr__ (self ):
240+ return 'BabelSubtypeField({!r}, {!r})' .format (
241+ self .name ,
242+ self .type_ref ,
243+ )
244+
175245class BabelRouteDef (_Element ):
246+
176247 def __init__ (self , lineno , lexpos , name , request_type_ref ,
177248 response_type_ref , error_type_ref = None ):
178249 super (BabelRouteDef , self ).__init__ (lineno , lexpos )
@@ -397,41 +468,63 @@ def p_tag_ref(self, p):
397468 #
398469 # typed_field String
399470 # "This is a docstring for the field"
471+ #
472+ # An example struct that enumerates subtypes looks as follows:
473+ #
474+ # struct P
475+ # union
476+ # t1 S1
477+ # t2 S2
478+ # field String
479+ #
480+ # struct S1 extends P
481+ # ...
482+ #
483+ # struct S2 extends P
484+ # ...
485+ #
486+
487+ def p_enumerated_subtypes (self , p ):
488+ """enumerated_subtypes : UNION asterix_option NEWLINE INDENT subtypes_list DEDENT
489+ | empty"""
490+ if len (p ) > 2 :
491+ p [0 ] = (p [5 ], p [2 ])
492+
493+ def p_struct (self , p ):
494+ """struct : STRUCT ID inheritance NEWLINE \
495+ INDENT docsection enumerated_subtypes field_list example_list DEDENT"""
496+ p [0 ] = BabelStructDef (
497+ lineno = p .lineno (2 ),
498+ lexpos = p .lexpos (2 ),
499+ name = p [2 ],
500+ extends = p [3 ],
501+ doc = p [6 ],
502+ subtypes = p [7 ],
503+ fields = p [8 ])
504+ if p [9 ] is not None :
505+ for label , text , example in p [9 ]:
506+ p [0 ].add_example (label , text , example )
400507
401508 def p_inheritance (self , p ):
402509 """inheritance : EXTENDS ID
403510 | empty"""
404511 if p [1 ]:
405512 p [0 ] = p [2 ]
406513
407- def p_coverage_list_create (self , p ):
408- 'coverage_list : ID'
409- p [0 ] = [p [1 ]]
514+ def p_enumerated_subtypes_list_create (self , p ):
515+ """subtypes_list : subtype_field
516+ | empty"""
517+ if p [1 ] is not None :
518+ p [0 ] = [p [1 ]]
410519
411- def p_coverage_list_extend (self , p ):
412- 'coverage_list : coverage_list PIPE ID '
520+ def p_enumerated_subtypes_list_extend (self , p ):
521+ 'subtypes_list : subtypes_list subtype_field '
413522 p [0 ] = p [1 ]
414- p [0 ].append (p [3 ])
523+ p [0 ].append (p [2 ])
415524
416- def p_coverage (self , p ):
417- """coverage : OF coverage_list
418- | empty"""
419- if p [1 ]:
420- p [0 ] = p [2 ]
421-
422- def p_struct (self , p ):
423- """
424- struct : STRUCT ID inheritance coverage NEWLINE \
425- INDENT docsection field_list example_list DEDENT
426- """
427- p [0 ] = BabelTypeDef (p .lineno (1 ), p .lexpos (1 ), p [1 ], p [2 ], extends = p [3 ], coverage = p [4 ])
428- if p [7 ]:
429- p [0 ].set_doc (p [7 ])
430- if p [8 ] is not None :
431- p [0 ].set_fields (p [8 ])
432- if p [9 ] is not None :
433- for label , text , example in p [9 ]:
434- p [0 ].add_example (label , text , example )
525+ def p_enumerated_subtype_field (self , p ):
526+ 'subtype_field : ID type_ref NEWLINE'
527+ p [0 ] = BabelSubtypeField (p .lineno (1 ), p .lexpos (1 ), p [1 ], p [2 ])
435528
436529 # --------------------------------------------------------------
437530 # Fields
@@ -445,7 +538,9 @@ def p_struct(self, p):
445538 def p_field_list_create (self , p ):
446539 """field_list : field
447540 | empty"""
448- if p [1 ] is not None :
541+ if p [1 ] is None :
542+ p [0 ] = []
543+ else :
449544 p [0 ] = [p [1 ]]
450545
451546 def p_field_list_extend (self , p ):
@@ -497,11 +592,13 @@ def p_field(self, p):
497592
498593 def p_union (self , p ):
499594 'union : UNION ID inheritance NEWLINE INDENT docsection field_list example_list DEDENT'
500- p [0 ] = BabelTypeDef (p .lineno (1 ), p .lexpos (1 ), p [1 ], p [2 ], extends = p [3 ])
501- if p [6 ]:
502- p [0 ].set_doc (p [6 ])
503- if p [7 ] is not None :
504- p [0 ].set_fields (p [7 ])
595+ p [0 ] = BabelUnionDef (
596+ lineno = p .lineno (1 ),
597+ lexpos = p .lexpos (1 ),
598+ name = p [2 ],
599+ extends = p [3 ],
600+ doc = p [6 ],
601+ fields = p [7 ])
505602 if p [8 ]:
506603 for label , text , example in p [8 ]:
507604 p [0 ].add_example (label , text , example )
@@ -580,7 +677,7 @@ def p_attrs_section(self, p):
580677 def p_docsection (self , p ):
581678 """docsection : docstring NEWLINE
582679 | empty"""
583- if p [1 ]:
680+ if p [1 ] is not None :
584681 p [0 ] = p [1 ]
585682
586683 def p_docstring_string (self , p ):
0 commit comments