Skip to content

Commit 18842b0

Browse files
committed
Implemented strunions as "enumerated subtypes."
Summary: - A strunion is declared in babel by declaring a union within a struct definition. - A subtype is represented in JSON as a field of an object with the value being another object containing the fields specific to that subtype. Test Plan: - Added babel and python generator tests. - Updated server codebase to use this feature, and it works as expected. Reviewed By: guido
1 parent b224571 commit 18842b0

File tree

13 files changed

+1437
-245
lines changed

13 files changed

+1437
-245
lines changed

babelapi/babel/lexer.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ def test(self, data):
9797
'ID',
9898
'KEYWORD',
9999
'PATH',
100-
'PIPE',
101100
'DOT',
102101
)
103102

@@ -136,7 +135,6 @@ def test(self, data):
136135
t_RPAR = r'\)'
137136
t_EQ = r'='
138137
t_COMMA = r','
139-
t_PIPE = r'\|'
140138
t_ASTERIX = r'\*'
141139
t_Q = r'\?'
142140

@@ -150,7 +148,6 @@ def test(self, data):
150148
'attrs',
151149
'include',
152150
'namespace',
153-
'of',
154151
'pass',
155152
'request',
156153
'response',
@@ -164,7 +161,6 @@ def test(self, data):
164161
'extends': 'EXTENDS',
165162
'attrs': 'ATTRS',
166163
'include': 'INCLUDE',
167-
'of': 'OF',
168164
'pass': 'PASS',
169165
'route': 'ROUTE',
170166
'struct': 'STRUCT',

babelapi/babel/parser.py

Lines changed: 142 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,27 @@
22

33
from collections import OrderedDict
44
import logging
5+
import six
56

67
import ply.yacc as yacc
78

89
from babelapi.babel.lexer import BabelLexer, BabelNull
910

1011
class _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

1524
class 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

3040
class 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

4556
class 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

5971
class 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

89142
class BabelTypeRef(_Element):
143+
90144
def __init__(self, lineno, lexpos, name, args, nullable):
91145
"""
92146
Args:
@@ -107,6 +161,7 @@ def __repr__(self):
107161
)
108162

109163
class 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

159215
class 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+
175245
class 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

Comments
 (0)