OILS / asdl / gen_python.py View on Github | oils.pub

721 lines, 459 significant
1#!/usr/bin/env python2
2"""gen_python.py: Generate Python code from an ASDL schema."""
3from __future__ import print_function
4
5from collections import defaultdict
6
7from asdl import ast
8from asdl import visitor
9from asdl.util import log
10
11from typing import List, Dict, Optional, Union, Tuple, IO, Any, cast
12
13_ = log # shut up lint
14
15_PRIMITIVES = {
16 'string': 'str',
17 'int': 'int',
18 'uint16': 'int',
19 'BigInt': 'mops.BigInt',
20 'float': 'float',
21 'bool': 'bool',
22 'any': 'Any',
23}
24
25
26def _MyPyType(typ):
27 # type: (ast.type_expr_t) -> str
28 """ASDL type to MyPy Type."""
29 if isinstance(typ, ast.ParameterizedType):
30
31 if typ.type_name == 'Dict':
32 k_type = _MyPyType(typ.children[0])
33 v_type = _MyPyType(typ.children[1])
34 return 'Dict[%s, %s]' % (k_type, v_type)
35
36 if typ.type_name == 'List':
37 return 'List[%s]' % _MyPyType(typ.children[0])
38
39 if typ.type_name == 'Optional':
40 return 'Optional[%s]' % _MyPyType(typ.children[0])
41
42 raise AssertionError()
43
44 elif isinstance(typ, ast.NamedType):
45 if typ.resolved:
46 if isinstance(typ.resolved, ast.Sum): # includes SimpleSum
47 return '%s_t' % typ.name
48 if isinstance(typ.resolved, ast.Product):
49 return typ.name
50 if isinstance(typ.resolved, ast.Use):
51 py_name, _ = ast.TypeNameHeuristic(typ.name)
52 return py_name
53 if isinstance(typ.resolved, ast.Extern):
54 r = typ.resolved
55 type_name = r.names[-1]
56 py_module = r.names[-2]
57 return '%s.%s' % (py_module, type_name)
58 raise AssertionError(typ)
59
60 return _PRIMITIVES[typ.name]
61
62 else:
63 raise AssertionError()
64
65
66def _CastedNull(mypy_type):
67 # type: (str) -> str
68 return "cast('%s', None)" % mypy_type
69
70
71def _DefaultValue(typ, mypy_type):
72 # type: (ast.type_expr_t, str) -> str
73 """Values that the static CreateNull() constructor passes.
74
75 mypy_type is used to cast None, to maintain mypy --strict for ASDL.
76
77 We circumvent the type system on CreateNull(). Then the user is
78 responsible for filling in all the fields. If they do so, we can
79 rely on it when reading fields at runtime.
80 """
81 if isinstance(typ, ast.ParameterizedType):
82 type_name = typ.type_name
83
84 if type_name == 'Optional':
85 return _CastedNull(mypy_type)
86
87 if type_name == 'List':
88 return "[] if alloc_lists else cast('%s', None)" % mypy_type
89
90 if type_name == 'Dict': # TODO: can respect alloc_dicts=True
91 return _CastedNull(mypy_type)
92
93 raise AssertionError(type_name)
94
95 if isinstance(typ, ast.NamedType):
96 type_name = typ.name
97
98 if type_name == 'id': # hard-coded HACK
99 return '-1'
100
101 if type_name == 'int':
102 return '-1'
103
104 if type_name == 'BigInt':
105 return 'mops.BigInt(-1)'
106
107 if type_name == 'bool':
108 return 'False'
109
110 if type_name == 'float':
111 return '0.0' # or should it be NaN?
112
113 if type_name == 'string':
114 return "''"
115
116 if isinstance(typ.resolved, ast.SimpleSum):
117 sum_type = typ.resolved
118 # Just make it the first variant. We could define "Undef" for
119 # each enum, but it doesn't seem worth it.
120 return '%s_e.%s' % (type_name, sum_type.types[0].name)
121
122 # CompoundSum or Product type
123 return _CastedNull(mypy_type)
124
125 else:
126 raise AssertionError()
127
128
129def _HNodeExpr(typ, var_name):
130 # type: (ast.type_expr_t, str) -> Tuple[str, bool]
131 none_guard = False
132
133 if ast.IsOptional(typ):
134 narrow = cast(ast.ParameterizedType, typ)
135 typ = narrow.children[0] # descend one level
136
137 if isinstance(typ, ast.ParameterizedType):
138 code_str = '%s.PrettyTree()' % var_name
139 none_guard = True
140
141 elif isinstance(typ, ast.NamedType):
142 type_name = typ.name
143
144 if type_name == 'bool':
145 code_str = "hnode.Leaf('T' if %s else 'F', color_e.OtherConst)" % var_name
146
147 elif type_name in ('int', 'uint16'):
148 code_str = 'hnode.Leaf(str(%s), color_e.OtherConst)' % var_name
149
150 elif type_name == 'BigInt':
151 code_str = 'hnode.Leaf(mops.ToStr(%s), color_e.OtherConst)' % var_name
152
153 elif type_name == 'float':
154 code_str = 'hnode.Leaf(str(%s), color_e.OtherConst)' % var_name
155
156 elif type_name == 'string':
157 code_str = 'NewLeaf(%s, color_e.StringConst)' % var_name
158
159 elif type_name == 'any':
160 code_str = 'NewLeaf(str(%s), color_e.External)' % var_name
161
162 elif type_name == 'id': # was meta.UserType
163 # This assumes it's Id, which is a simple SumType. TODO: Remove this.
164 code_str = 'hnode.Leaf(Id_str(%s, dot=False), color_e.UserType)' % var_name
165
166 elif typ.resolved and isinstance(typ.resolved, ast.SimpleSum):
167 code_str = 'hnode.Leaf(%s_str(%s), color_e.TypeName)' % (type_name,
168 var_name)
169
170 else:
171 code_str = '%s.PrettyTree(do_abbrev, trav=trav)' % var_name
172 none_guard = True
173
174 else:
175 raise AssertionError()
176
177 return code_str, none_guard
178
179
180class GenMyPyVisitor(visitor.AsdlVisitor):
181 """Generate Python code with MyPy type annotations."""
182
183 def __init__(
184 self,
185 f, # type: IO[bytes]
186 abbrev_module=None, # type: Optional[str]
187 abbrev_mod_entries=None, # type: Optional[List[str]]
188 pretty_print_methods=True, # type: bool
189 py_init_n=False, # type: bool
190 ):
191 # type: (...) -> None
192
193 visitor.AsdlVisitor.__init__(self, f)
194 self.abbrev_module = abbrev_module
195 self.abbrev_mod_entries = abbrev_mod_entries or []
196 self.pretty_print_methods = pretty_print_methods
197 self.py_init_n = py_init_n
198
199 self._shared_type_tags = {} # type: Dict[str, int]
200 self._product_counter = 64 # matches asdl/gen_cpp.py
201
202 self._products = [] # type: List[Any]
203 self._base_classes = defaultdict(list) # type: Dict[str, List[str]]
204
205 self._subtypes = [] # type: List[Any]
206
207 def EmitHeader(self, schema_ast):
208 # type: (ast.Module) -> None
209 f = self.f
210
211 # TODO: Remove Any once we stop using it
212 f.write("""\
213from asdl import pybase
214from mycpp import mops
215from typing import Optional, List, Tuple, Dict, Any, cast, TYPE_CHECKING
216""")
217
218 for use in schema_ast.uses:
219 # HACK
220 if use.module_parts == ['frontend', 'id_kind']:
221 f.write('from _devbuild.gen.id_kind_asdl import Id_str\n')
222
223 if schema_ast.uses:
224 f.write('\n')
225 f.write('if TYPE_CHECKING:\n')
226 for use in schema_ast.uses:
227 py_names = []
228 for n in use.type_names:
229 py_name, _ = ast.TypeNameHeuristic(n)
230 py_names.append(py_name)
231
232 # indented
233 f.write(' from _devbuild.gen.%s_asdl import %s\n' %
234 (use.module_parts[-1], ', '.join(py_names)))
235
236 if schema_ast.externs:
237 f.write('\n')
238 f.write('if TYPE_CHECKING:\n')
239 for extern in schema_ast.externs:
240 names = extern.names
241 mod_parts = names[:-2]
242 f.write(' from %s import %s\n' % ('.'.join(mod_parts), names[-2]))
243
244 if self.pretty_print_methods:
245 f.write("""
246from asdl import runtime # For runtime.NO_SPID
247from asdl.runtime import NewRecord, NewLeaf, TraversalState
248from _devbuild.gen.hnode_asdl import color_e, hnode, hnode_e, hnode_t, Field
249
250""")
251 if self.abbrev_module:
252 f.write('from %s import *\n' % self.abbrev_module)
253 f.write('\n')
254
255 def _EmitDict(self, name, d, depth):
256 # type: (str, Dict[int, str], int) -> None
257 self.Emit('_%s_str = {' % name, depth)
258 for k in sorted(d):
259 self.Emit('%d: %r,' % (k, d[k]), depth + 1)
260 self.Emit('}', depth)
261 self.Emit('', depth)
262
263 def VisitSimpleSum(self, sum, sum_name, depth):
264 # type: (ast.SimpleSum, str, int) -> None
265 int_to_str = {}
266 variants = []
267 for i, variant in enumerate(sum.types):
268 tag_num = i + 1
269 int_to_str[tag_num] = variant.name
270 variants.append((variant, tag_num))
271
272 add_suffix = not ('no_namespace_suffix' in sum.generate)
273 gen_integers = 'integers' in sum.generate or 'uint16' in sum.generate
274
275 if gen_integers:
276 self.Emit('%s_t = int # type alias for integer' % sum_name)
277 self.Emit('')
278
279 i_name = ('%s_i' % sum_name) if add_suffix else sum_name
280
281 self.Emit('class %s(object):' % i_name, depth)
282
283 for variant, tag_num in variants:
284 line = ' %s = %d' % (variant.name, tag_num)
285 self.Emit(line, depth)
286
287 # Help in sizing array. Note that we're 1-based.
288 line = ' %s = %d' % ('ARRAY_SIZE', len(variants) + 1)
289 self.Emit(line, depth)
290
291 else:
292 # First emit a type
293 self.Emit('class %s_t(pybase.SimpleObj):' % sum_name, depth)
294 self.Emit(' pass', depth)
295 self.Emit('', depth)
296
297 # Now emit a namespace
298 e_name = ('%s_e' % sum_name) if add_suffix else sum_name
299 self.Emit('class %s(object):' % e_name, depth)
300
301 for variant, tag_num in variants:
302 line = ' %s = %s_t(%d)' % (variant.name, sum_name, tag_num)
303 self.Emit(line, depth)
304
305 self.Emit('', depth)
306
307 self._EmitDict(sum_name, int_to_str, depth)
308
309 sum_name = ast.NameHack(sum_name)
310
311 self.Emit('def %s_str(val, dot=True):' % sum_name, depth)
312 self.Emit(' # type: (%s_t, bool) -> str' % sum_name, depth)
313 self.Emit(' v = _%s_str[val]' % sum_name, depth)
314 self.Emit(' if dot:', depth)
315 self.Emit(' return "%s.%%s" %% v' % sum_name, depth)
316 self.Emit(' else:', depth)
317 self.Emit(' return v', depth)
318 self.Emit('', depth)
319
320 def _EmitCodeForField(self, field, counter):
321 # type: (ast.Field, int) -> None
322 """Generate code that returns an hnode for a field."""
323 out_val_name = 'x%d' % counter
324
325 if ast.IsList(field.typ):
326 iter_name = 'i%d' % counter
327
328 typ = field.typ
329 if ast.IsOptional(typ): # descend one level
330 typ = cast(ast.ParameterizedType, typ).children[0]
331 item_type = cast(ast.ParameterizedType, typ).children[0]
332
333 self.Emit(' if self.%s is not None: # List' % field.name)
334 self.Emit(' %s = hnode.Array([])' % out_val_name)
335 self.Emit(' for %s in self.%s:' % (iter_name, field.name))
336 child_code_str, none_guard = _HNodeExpr(item_type, iter_name)
337
338 if none_guard: # e.g. for List[Optional[value_t]]
339 # TODO: could consolidate with asdl/runtime.py NewLeaf(), which
340 # also uses _ to mean None/nullptr
341 self.Emit(
342 ' h = (hnode.Leaf("_", color_e.OtherConst) if %s is None else %s)'
343 % (iter_name, child_code_str))
344 self.Emit(' %s.children.append(h)' % out_val_name)
345 else:
346 self.Emit(' %s.children.append(%s)' %
347 (out_val_name, child_code_str))
348
349 self.Emit(' L.append(Field(%r, %s))' %
350 (field.name, out_val_name))
351
352 elif ast.IsDict(field.typ):
353 k = 'k%d' % counter
354 v = 'v%d' % counter
355
356 typ = field.typ
357 if ast.IsOptional(typ): # descend one level
358 typ = cast(ast.ParameterizedType, typ).children[0]
359
360 k_typ = cast(ast.ParameterizedType, typ).children[0]
361 v_typ = cast(ast.ParameterizedType, typ).children[1]
362
363 k_code_str, _ = _HNodeExpr(k_typ, k)
364 v_code_str, _ = _HNodeExpr(v_typ, v)
365
366 unnamed = 'unnamed%d' % counter
367 self.Emit(' if self.%s is not None: # Dict' % field.name)
368 self.Emit(' %s = [] # type: List[hnode_t]' % unnamed)
369 self.Emit(' %s = hnode.Record("", "{", "}", [], %s)' %
370 (out_val_name, unnamed))
371 self.Emit(' for %s, %s in self.%s.iteritems():' %
372 (k, v, field.name))
373 self.Emit(' %s.append(%s)' % (unnamed, k_code_str))
374 self.Emit(' %s.append(%s)' % (unnamed, v_code_str))
375 self.Emit(' L.append(Field(%r, %s))' %
376 (field.name, out_val_name))
377
378 elif ast.IsOptional(field.typ):
379 typ = cast(ast.ParameterizedType, field.typ).children[0]
380
381 self.Emit(' if self.%s is not None: # Optional' % field.name)
382 child_code_str, _ = _HNodeExpr(typ, 'self.%s' % field.name)
383 self.Emit(' %s = %s' % (out_val_name, child_code_str))
384 self.Emit(' L.append(Field(%r, %s))' %
385 (field.name, out_val_name))
386
387 else:
388 var_name = 'self.%s' % field.name
389 code_str, obj_none_guard = _HNodeExpr(field.typ, var_name)
390 depth = self.current_depth
391 if obj_none_guard: # to satisfy MyPy type system
392 self.Emit(' assert self.%s is not None' % field.name)
393 self.Emit(' %s = %s' % (out_val_name, code_str), depth)
394
395 self.Emit(' L.append(Field(%r, %s))' % (field.name, out_val_name),
396 depth)
397
398 def _GenClassBegin(self, class_name, base_classes, tag_num):
399 # type: (str, Union[List[str], Tuple[str]], int) -> None
400 self.Emit('class %s(%s):' % (class_name, ', '.join(base_classes)))
401 self.Emit(' _type_tag = %d' % tag_num)
402
403 def _GenListSubclass(self, class_name, base_classes, tag_num, class_ns=''):
404 # type: (str, List[str], int, str) -> None
405 self._GenClassBegin(class_name, base_classes, tag_num)
406
407 # TODO: Do something nicer
408 base_class_str = [b for b in base_classes if b.startswith('List[')][0]
409
410 # Needed for c = CompoundWord() to work
411 # TODO: make it
412 # c = CompoundWord.New()
413 if 0:
414 self.Emit(' def __init__(self, other=None):')
415 self.Emit(' # type: (Optional[%s]) -> None' % base_class_str,
416 reflow=False)
417 self.Emit(' if other is not None:')
418 self.Emit(' self.extend(other)')
419 self.Emit('')
420
421 # Use our own constructor
422 self.Emit(' @staticmethod')
423 self.Emit(' def New():')
424 self.Emit(' # type: () -> %s' % class_name)
425 self.Emit(' return %s()' % class_name)
426 self.Emit('')
427
428 self.Emit(' @staticmethod')
429 self.Emit(' def Take(plain_list):')
430 self.Emit(' # type: (%s) -> %s' % (base_class_str, class_name))
431 self.Emit(' result = %s(plain_list)' % class_name)
432 self.Emit(' del plain_list[:]')
433 self.Emit(' return result')
434 self.Emit('')
435
436 if self.pretty_print_methods:
437 self._EmitPrettyPrintMethodsForList(class_name)
438
439 def _GenClass(
440 self,
441 fields, # type: List[ast.Field]
442 class_name, # type: str
443 base_classes, # type: Union[List[str], Tuple[str]]
444 tag_num, # type: int
445 class_ns='', # type: str
446 ):
447 # type: (...) -> None
448 """Generate a typed Python class.
449
450 Used for both Sum variants ("constructors") and Product types.
451
452 Args:
453 class_ns: for variants like value.Str
454 """
455 self._GenClassBegin(class_name, base_classes, tag_num)
456
457 field_names = [f.name for f in fields]
458
459 quoted_fields = repr(tuple(field_names))
460 self.Emit(' __slots__ = %s' % quoted_fields)
461 self.Emit('')
462
463 #
464 # __init__
465 #
466
467 args = [f.name for f in fields]
468 arg_types = []
469 default_vals = []
470 for f in fields:
471 mypy_type = _MyPyType(f.typ)
472 arg_types.append(mypy_type)
473
474 d_str = _DefaultValue(f.typ, mypy_type)
475 default_vals.append(d_str)
476
477 self.Emit(' def __init__(self, %s):' % ', '.join(args))
478 self.Emit(' # type: (%s) -> None' % ', '.join(arg_types),
479 reflow=False)
480
481 if not fields:
482 self.Emit(' pass') # for types like NoOp
483
484 for f in fields:
485 # don't wrap the type comment
486 self.Emit(' self.%s = %s' % (f.name, f.name), reflow=False)
487
488 self.Emit('')
489
490 # CreateNull() - another way of initializing
491 if len(fields) and not self.py_init_n:
492 self.Emit(' @staticmethod')
493 self.Emit(' def CreateNull(alloc_lists=False):')
494 self.Emit(' # type: () -> %s%s' % (class_ns, class_name))
495 self.Emit(' return %s%s(%s)' %
496 (class_ns, class_name, ', '.join(default_vals)),
497 reflow=False)
498 self.Emit('')
499
500 # PrettyTree()
501 if self.pretty_print_methods:
502 self._EmitPrettyPrintMethods(class_name, class_ns, fields)
503
504 def _EmitPrettyBegin(self):
505 # type: () -> None
506 self.Emit(' def PrettyTree(self, do_abbrev, trav=None):')
507 self.Emit(' # type: (bool, Optional[TraversalState]) -> hnode_t')
508 self.Emit(' trav = trav or TraversalState()')
509 self.Emit(' heap_id = id(self)')
510 self.Emit(' if heap_id in trav.seen:')
511 # cut off recursion
512 self.Emit(' return hnode.AlreadySeen(heap_id)')
513 self.Emit(' trav.seen[heap_id] = True')
514
515 def _EmitPrettyPrintMethodsForList(self, class_name):
516 # type: (str) -> None
517 self._EmitPrettyBegin()
518 self.Emit(' h = runtime.NewRecord(%r)' % class_name)
519 self.Emit(
520 ' h.unnamed_fields = [c.PrettyTree(do_abbrev) for c in self]')
521 self.Emit(' return h')
522 self.Emit('')
523
524 def _EmitPrettyPrintMethods(self, class_name, class_ns, fields):
525 # type: (str, str, List[ast.Field]) -> None
526 if len(fields) == 0:
527 # value__Stdin -> value.Stdin (defined at top level)
528 pretty_cls_name = class_name.replace('__', '.')
529 else:
530 # value.Str (defined inside the 'class value') namespace
531 pretty_cls_name = '%s%s' % (class_ns, class_name)
532
533 # def PrettyTree(...):
534
535 self._EmitPrettyBegin()
536 self.Emit('')
537
538 if class_ns:
539 # e.g. _command__Simple
540 assert class_ns.endswith('.')
541 abbrev_name = '_%s__%s' % (class_ns[:-1], class_name)
542 else:
543 # e.g. _Token
544 abbrev_name = '_%s' % class_name
545
546 if abbrev_name in self.abbrev_mod_entries:
547 self.Emit(' if do_abbrev:')
548 self.Emit(' p = %s(self)' % abbrev_name)
549 self.Emit(' if p:')
550 self.Emit(' return p')
551 self.Emit('')
552
553 self.Emit(' out_node = NewRecord(%r)' % pretty_cls_name)
554 self.Emit(' L = out_node.fields')
555 self.Emit('')
556
557 # Use the runtime type to be more like asdl/format.py
558 for local_id, field in enumerate(fields):
559 #log('%s :: %s', field_name, field_desc)
560 self.Indent()
561 self._EmitCodeForField(field, local_id)
562 self.Dedent()
563 self.Emit('')
564 self.Emit(' return out_node')
565 self.Emit('')
566
567 def VisitCompoundSum(self, sum, sum_name, depth):
568 # type: (ast.Sum, str, int) -> None
569 """Note that the following is_simple:
570
571 cflow = Break | Continue
572
573 But this is compound:
574
575 cflow = Break | Continue | Return(int val)
576
577 The generated code changes depending on which one it is.
578 """
579 #log('%d variants in %s', len(sum.types), sum_name)
580
581 # We emit THREE Python types for each meta.CompoundType:
582 #
583 # 1. enum for tag (cflow_e)
584 # 2. base class for inheritance (cflow_t)
585 # 3. namespace for classes (cflow) -- TODO: Get rid of this one.
586 #
587 # Should code use cflow_e.tag or isinstance()?
588 # isinstance() is better for MyPy I think. But tag is better for C++.
589 # int tag = static_cast<cflow>(node).tag;
590
591 int_to_str = {}
592
593 # enum for the tag
594 self.Emit('class %s_e(object):' % sum_name, depth)
595
596 for i, variant in enumerate(sum.types):
597 if variant.shared_type:
598 tag_num = self._shared_type_tags[variant.shared_type]
599 # e.g. DoubleQuoted may have base types expr_t, word_part_t
600 base_class = sum_name + '_t'
601 bases = self._base_classes[variant.shared_type]
602 if base_class in bases:
603 raise RuntimeError(
604 "Two tags in sum %r refer to product type %r" %
605 (sum_name, variant.shared_type))
606
607 else:
608 bases.append(base_class)
609 else:
610 tag_num = i + 1
611 self.Emit(' %s = %d' % (variant.name, tag_num), depth)
612 int_to_str[tag_num] = variant.name
613 self.Emit('', depth)
614
615 self._EmitDict(sum_name, int_to_str, depth)
616
617 sum_name = ast.NameHack(sum_name)
618
619 self.Emit('def %s_str(tag, dot=True):' % sum_name, depth)
620 self.Emit(' # type: (int, bool) -> str', depth)
621 self.Emit(' v = _%s_str[tag]' % sum_name, depth)
622 self.Emit(' if dot:', depth)
623 self.Emit(' return "%s.%%s" %% v' % sum_name, depth)
624 self.Emit(' else:', depth)
625 self.Emit(' return v', depth)
626 self.Emit('', depth)
627
628 # the base class, e.g. 'command_t'
629 self.Emit('class %s_t(pybase.CompoundObj):' % sum_name, depth)
630 self.Indent()
631 depth = self.current_depth
632
633 # To imitate C++ API
634 self.Emit('def tag(self):')
635 self.Emit(' # type: () -> int')
636 self.Emit(' return self._type_tag')
637
638 self.Dedent()
639 depth = self.current_depth
640
641 self.Emit('')
642
643 # Declare any zero argument singleton classes outside of the main
644 # "namespace" class.
645 for i, variant in enumerate(sum.types):
646 if variant.shared_type:
647 continue # Don't generate a class for shared types.
648 if len(variant.fields) == 0:
649 # We must use the old-style naming here, ie. command__NoOp, in order
650 # to support zero field variants as constants.
651 class_name = '%s__%s' % (sum_name, variant.name)
652 self._GenClass(variant.fields, class_name, (sum_name + '_t', ),
653 i + 1)
654
655 # Class that's just a NAMESPACE, e.g. for value.Str
656 self.Emit('class %s(object):' % sum_name, depth)
657
658 self.Indent()
659
660 for i, variant in enumerate(sum.types):
661 if variant.shared_type:
662 continue
663
664 if len(variant.fields) == 0:
665 self.Emit('%s = %s__%s()' %
666 (variant.name, sum_name, variant.name))
667 self.Emit('')
668 else:
669 # Use fully-qualified name, so we can have osh_cmd.Simple and
670 # oil_cmd.Simple.
671 fq_name = variant.name
672 self._GenClass(variant.fields,
673 fq_name, (sum_name + '_t', ),
674 i + 1,
675 class_ns=sum_name + '.')
676 self.Emit(' pass', depth) # in case every variant is first class
677
678 self.Dedent()
679 self.Emit('')
680
681 def VisitSubType(self, subtype):
682 # type: (ast.SubTypeDecl) -> None
683 self._shared_type_tags[subtype.name] = self._product_counter
684
685 # Also create these last. They may inherit from sum types that have yet
686 # to be defined.
687 self._subtypes.append((subtype, self._product_counter))
688 self._product_counter += 1
689
690 def VisitProduct(self, product, name, depth):
691 # type: (ast.Product, str, int) -> None
692 self._shared_type_tags[name] = self._product_counter
693 # Create a tuple of _GenClass args to create LAST. They may inherit from
694 # sum types that have yet to be defined.
695 self._products.append((product, name, depth, self._product_counter))
696 self._product_counter += 1
697
698 def EmitFooter(self):
699 # type: () -> None
700 # Now generate all the product types we deferred.
701 for args in self._products:
702 ast_node, name, depth, tag_num = args
703 # Figure out base classes AFTERWARD.
704 bases = self._base_classes[name]
705 if not bases:
706 bases = ['pybase.CompoundObj']
707 self._GenClass(ast_node.fields, name, bases, tag_num)
708
709 for args in self._subtypes:
710 subtype, tag_num = args
711 # Figure out base classes AFTERWARD.
712 bases = self._base_classes[subtype.name]
713 if not bases:
714 bases = ['pybase.CompoundObj']
715
716 bases.append(_MyPyType(subtype.base_class))
717
718 if ast.IsList(subtype.base_class):
719 self._GenListSubclass(subtype.name, bases, tag_num)
720 else:
721 self._GenClass([], subtype.name, bases, tag_num)