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

951 lines, 620 significant
1"""
2gen_cpp.py - Generate C++ classes from an ASDL schema.
3
4TODO:
5
6- Integrate some of the lessons here:
7 - https://github.com/oilshell/blog-code/tree/master/asdl
8 - And maybe mycpp/target_lang.cc
9
10- pretty printing methods
11 - so asdl/format.py get translated?
12
13- NoOp needs to be instantiated without args?
14- dict becomes Dict[str, str] ?
15
16- How do optional ASDL values like int? work? Use C++ default values?
17 - This means that all the optionals have to be on the end. That seems OK.
18 - I guess that's how Python does it.
19"""
20from __future__ import print_function
21
22import sys
23
24from collections import defaultdict
25
26from asdl import ast
27from asdl import visitor
28from asdl.util import log
29
30from typing import List, Dict, Tuple, IO, Union, Optional, Any, cast
31
32_ = log
33
34
35# Used by core/asdl_gen.py to generate _devbuild/gen/osh-types.h, with
36# lex_mode__*
37class CEnumVisitor(visitor.AsdlVisitor):
38
39 def VisitSimpleSum(self, sum, name, depth):
40 # Just use #define, since enums aren't namespaced.
41 for i, variant in enumerate(sum.types):
42 self.Emit('#define %s__%s %d' % (name, variant.name, i + 1), depth)
43 self.Emit("", depth)
44
45
46_PRIMITIVES = {
47 'string': 'BigStr*', # declared in containers.h
48 'int': 'int',
49 'uint16': 'uint16_t',
50 'BigInt': 'mops::BigInt',
51 'float': 'double',
52 'bool': 'bool',
53 'any': 'void*',
54}
55
56
57class ForwardDeclareVisitor(visitor.AsdlVisitor):
58 """Print forward declarations.
59
60 ASDL allows forward references of types, but C++ doesn't.
61 """
62
63 def VisitCompoundSum(self, sum, name, depth):
64 # type: (ast.Sum, str, int) -> None
65 self.Emit("class %(name)s_t;" % locals(), depth)
66
67 def VisitProduct(self, product, name, depth):
68 # type: (ast.Product, str, int) -> None
69 self.Emit("class %(name)s;" % locals(), depth)
70
71 def EmitFooter(self):
72 # type: () -> None
73 self.Emit("", 0) # blank line
74
75
76def _GetCppType(typ):
77 # type: (ast.type_expr_t) -> str
78 if isinstance(typ, ast.ParameterizedType):
79 type_name = typ.type_name
80
81 if type_name == 'Dict':
82 k_type = _GetCppType(typ.children[0])
83 v_type = _GetCppType(typ.children[1])
84 return 'Dict<%s, %s>*' % (k_type, v_type)
85
86 if type_name == 'List':
87 c_type = _GetCppType(typ.children[0])
88 return 'List<%s>*' % (c_type)
89
90 if type_name == 'Optional':
91 c_type = _GetCppType(typ.children[0])
92 return c_type
93
94 raise AssertionError()
95
96 elif isinstance(typ, ast.NamedType):
97
98 if typ.resolved:
99 if isinstance(typ.resolved, ast.SimpleSum):
100 return '%s_t' % typ.name
101 if isinstance(typ.resolved, ast.Sum):
102 return '%s_t*' % typ.name
103 if isinstance(typ.resolved, ast.Product):
104 return '%s*' % typ.name
105 if isinstance(typ.resolved, ast.Use):
106 py_name, is_pointer = ast.TypeNameHeuristic(typ.name)
107 star = '*' if is_pointer else ''
108 return '%s_asdl::%s%s' % (typ.resolved.module_parts[-1],
109 py_name, star)
110 if isinstance(typ.resolved, ast.Extern):
111 r = typ.resolved
112 type_name = r.names[-1]
113 cpp_namespace = r.names[-2]
114 return '%s::%s*' % (cpp_namespace, type_name)
115 raise AssertionError()
116
117 return _PRIMITIVES[typ.name]
118
119 else:
120 raise AssertionError()
121
122
123def _IsManagedType(typ):
124 # type: (ast.type_expr_t) -> bool
125 # This is a little cheesy, but works
126 return _GetCppType(typ).endswith('*')
127
128
129def _DefaultValue(typ, conditional=True):
130 # type: (ast.type_expr_t, bool) -> str
131 """Values that the ::CreateNull() constructor passes."""
132
133 if isinstance(typ, ast.ParameterizedType):
134 type_name = typ.type_name
135
136 if type_name == 'Dict': # TODO: can respect alloc_dicts=True
137 return 'nullptr'
138
139 elif type_name == 'List':
140 c_type = _GetCppType(typ.children[0])
141
142 d = 'Alloc<List<%s>>()' % (c_type)
143 if conditional:
144 return 'alloc_lists ? %s : nullptr' % d
145 else:
146 return d
147
148 elif type_name == 'Optional':
149 return 'nullptr'
150
151 else:
152 raise AssertionError(type_name)
153
154 elif isinstance(typ, ast.NamedType):
155 type_name = typ.name
156
157 if type_name in ('int', 'uint16', 'BigInt'):
158 default = '-1'
159 elif type_name == 'id': # hard-coded HACK
160 default = '-1'
161 elif type_name == 'bool':
162 default = 'false'
163 elif type_name == 'float':
164 default = '0.0' # or should it be NaN?
165 elif type_name == 'string':
166 default = 'kEmptyString'
167
168 elif typ.resolved and isinstance(typ.resolved, ast.SimpleSum):
169 sum_type = typ.resolved
170 # Just make it the first variant. We could define "Undef" for
171 # each enum, but it doesn't seem worth it.
172 default = '%s_e::%s' % (type_name, sum_type.types[0].name)
173
174 else:
175 default = 'nullptr' # Sum or Product
176 return default
177
178 else:
179 raise AssertionError()
180
181
182def _HNodeExpr(typ, var_name):
183 # type: (ast.type_expr_t, str) -> Tuple[str, bool]
184 none_guard = False
185
186 if ast.IsOptional(typ):
187 narrow = cast(ast.ParameterizedType, typ)
188 typ = narrow.children[0] # descend one level
189
190 if isinstance(typ, ast.ParameterizedType):
191 code_str = '%s->PrettyTree()' % var_name
192 none_guard = True
193
194 elif isinstance(typ, ast.NamedType):
195
196 type_name = typ.name
197
198 if type_name in ('bool', 'int', 'uint16', 'BigInt', 'float', 'string'):
199 code_str = "ToPretty(%s)" % var_name
200
201 elif type_name == 'any':
202 # This is used for _BuiltinFunc, _BuiltinProc. There is not that much to customize here.
203 code_str = 'Alloc<hnode::Leaf>(StrFromC("<extern>"), color_e::External)' # % var_name
204
205 elif type_name == 'id': # was meta.UserType
206 code_str = 'Alloc<hnode::Leaf>(Id_str(%s, false), color_e::UserType)' % var_name
207
208 elif typ.resolved and isinstance(typ.resolved, ast.SimpleSum):
209 # ASDL could generate ToPretty<T> ?
210 code_str = 'Alloc<hnode::Leaf>(%s_str(%s), color_e::TypeName)' % (
211 type_name, var_name)
212
213 else:
214 code_str = '%s->PrettyTree(do_abbrev, seen)' % var_name
215 none_guard = True
216
217 else:
218 raise AssertionError()
219
220 return code_str, none_guard
221
222
223# Variant tags are NOT unique:
224# for each sum type, they go from 0, 1, 2 ... N
225# There can be up to 64
226# Product types tags ARE unique, because any of them can be SHARED between sum types
227# They start at 64
228MAX_VARIANTS_PER_SUM = 64
229
230
231class ClassDefVisitor(visitor.AsdlVisitor):
232 """Generate C++ declarations and type-safe enums."""
233
234 def __init__(
235 self,
236 f, # type: IO
237 pretty_print_methods=True, # type: bool
238 debug_info=None, # Optional[Dict[str, int]]
239 ):
240 # type: (...) -> None
241 """
242 Args:
243 f: file to write to
244 debug_info: dictionary fill in with info for GDB
245 """
246 visitor.AsdlVisitor.__init__(self, f)
247 self.pretty_print_methods = pretty_print_methods
248 self.debug_info = debug_info if debug_info is not None else {}
249
250 self._shared_type_tags = {} # type: Dict[str, int]
251 self._product_counter = MAX_VARIANTS_PER_SUM
252
253 self._products = [] # type: List[Any]
254 self._base_classes = defaultdict(list) # type: Dict[str, List[str]]
255
256 self._subtypes = [] # type: List[Any]
257
258 def _EmitEnum(self, sum, sum_name, depth, strong=False, is_simple=False):
259 # type: (ast.Sum, str, int, bool, bool) -> Dict[int, str]
260 enum = []
261 int_to_type = {}
262 add_suffix = not ('no_namespace_suffix' in sum.generate)
263 for i, variant in enumerate(sum.types):
264 if variant.shared_type: # Copied from gen_python.py
265 tag_num = self._shared_type_tags[variant.shared_type]
266 # e.g. DoubleQuoted may have base types expr_t, word_part_t
267 base_class = sum_name + '_t'
268 bases = self._base_classes[variant.shared_type]
269 if base_class in bases:
270 raise RuntimeError(
271 "Two tags in sum %r refer to product type %r" %
272 (sum_name, variant.shared_type))
273 else:
274 bases.append(base_class)
275 type_str = variant.shared_type
276 else:
277 tag_num = i + 1
278 type_str = '%s__%s' % (sum_name, variant.name)
279 int_to_type[tag_num] = type_str
280 enum.append((variant.name, tag_num)) # zero is reserved
281
282 if strong:
283 enum_name = '%s_e' % sum_name if add_suffix else sum_name
284
285 # Simple sum types can be STRONG since there's no possibility of multiple
286 # inheritance!
287
288 self.Emit('enum class %s {' % enum_name, depth)
289 for name, tag_num in enum:
290 self.Emit('%s = %d,' % (name, tag_num), depth + 1)
291 self.Emit('};', depth)
292
293 # type alias to match Python code
294 self.Emit('typedef %s %s_t;' % (enum_name, sum_name), depth)
295 self.Emit('', depth)
296
297 if self.pretty_print_methods:
298 sum_name = ast.NameHack(sum_name)
299 self.Emit(
300 'BigStr* %s_str(%s tag, bool dot = true);' %
301 (sum_name, enum_name), depth)
302 self.Emit('', depth)
303
304 else:
305 if is_simple:
306 enum_name = '%s_i' % sum_name if add_suffix else sum_name
307 else:
308 enum_name = '%s_e' % sum_name if add_suffix else sum_name
309
310 # Awkward struct/enum C++ idiom because:
311
312 # 1. namespace can't be "imported" with 'using'
313 # 2. plain enum pollutes outer namespace
314 # 3. C++ 11 'enum class' does not allow conversion to int
315 # 4. namespace and 'static const int' or 'static constexpr int' gives
316 # weird link errors
317 # https://quuxplusone.github.io/blog/2020/09/19/value-or-pitfall/
318
319 self.Emit('ASDL_NAMES %s {' % enum_name, depth)
320 self.Emit(' enum no_name {', depth)
321 for name, tag_num in enum:
322 self.Emit('%s = %d,' % (name, tag_num), depth + 1)
323
324 if is_simple:
325 # Help in sizing array. Note that we're 1-based.
326 self.Emit('ARRAY_SIZE = %d,' % (len(enum) + 1), depth + 1)
327
328 self.Emit(' };', depth)
329 self.Emit('};', depth)
330
331 self.Emit('', depth)
332
333 if self.pretty_print_methods:
334 sum_name = ast.NameHack(sum_name)
335 self.Emit(
336 'BigStr* %s_str(int tag, bool dot = true);' % sum_name,
337 depth)
338 self.Emit('', depth)
339
340 return int_to_type
341
342 def VisitSimpleSum(self, sum, name, depth):
343 # type: (ast.SimpleSum, str, int) -> None
344 # Note: there can be more than 128 variants in a simple sum, because it's an
345 # integer and doesn't have an object header.
346
347 if 'integers' in sum.generate:
348 self._EmitEnum(sum, name, depth, strong=False, is_simple=True)
349 self.Emit('typedef int %s_t;' % name)
350 self.Emit('')
351 elif 'uint16' in sum.generate:
352 self._EmitEnum(sum, name, depth, strong=False, is_simple=True)
353 self.Emit('typedef uint16_t %s_t;' % name)
354 self.Emit('')
355 else:
356 self._EmitEnum(sum, name, depth, strong=True)
357
358 def VisitCompoundSum(self, sum, sum_name, depth):
359 # type: (ast.Sum, str, int) -> None
360 #log('%d variants in %s', len(sum.types), sum_name)
361
362 # Must fit in 7 bit Obj::type_tag
363 assert len(sum.types) < MAX_VARIANTS_PER_SUM, \
364 'sum type %r has too many variants' % sum_name
365
366 # This is a sign that Python needs string interpolation!!!
367 def Emit(s, depth=depth):
368 # type: (str, int) -> None
369 self.Emit(s % sys._getframe(1).f_locals, depth)
370
371 int_to_type = self._EmitEnum(sum, sum_name, depth)
372
373 # Only add debug info for compound sums.
374 self.debug_info['%s_t' % sum_name] = int_to_type
375
376 # This is the base class.
377 Emit('class %(sum_name)s_t {')
378
379 # to implement Walkable, we'd need all types to fit in in the GC header
380 #Emit('class %(sum_name)s_t : public Walkable {')
381
382 # Can't be constructed directly. Note: this shows up in uftrace in debug
383 # mode, e.g. when we instantiate Token. Do we need it?
384 Emit(' protected:')
385 Emit(' %s_t() {' % sum_name)
386 Emit(' }')
387 Emit(' public:')
388 Emit(' int tag() const {')
389 # There's no inheritance relationship, so we have to reinterpret_cast.
390 Emit(' return ObjHeader::FromObject(this)->type_tag;')
391 Emit(' }')
392
393 if self.pretty_print_methods:
394 self.Emit(
395 ' hnode_t* PrettyTree(bool do_abbrev, Dict<int, bool>* seen = nullptr);'
396 )
397
398 Emit('')
399 Emit(' DISALLOW_COPY_AND_ASSIGN(%(sum_name)s_t)')
400 Emit('};')
401 Emit('')
402
403 for variant in sum.types:
404 if variant.shared_type:
405 continue # Don't generate a class.
406 super_name = '%s_t' % sum_name
407 tag = 'static_cast<uint16_t>(%s_e::%s)' % (sum_name, variant.name)
408 class_name = '%s__%s' % (sum_name, variant.name)
409 self._GenClass(variant.fields, class_name, [super_name], depth,
410 tag)
411
412 # Generate 'extern' declarations for zero arg singleton globals
413 for variant in sum.types:
414 if not variant.shared_type and len(variant.fields) == 0:
415 variant_name = variant.name
416 Emit(
417 'extern GcGlobal<%(sum_name)s__%(variant_name)s> g%(sum_name)s__%(variant_name)s;'
418 )
419
420 # Allow expr::Const in addition to expr.Const.
421 Emit('ASDL_NAMES %(sum_name)s {')
422 for variant in sum.types:
423 if variant.shared_type:
424 continue
425
426 # TODO: This produces a lint error, but IS USED via % reflection
427 variant_name = variant.name
428
429 if len(variant.fields) == 0:
430 Emit(
431 ' static %(sum_name)s__%(variant_name)s* %(variant_name)s;'
432 )
433 else:
434 Emit(
435 ' typedef %(sum_name)s__%(variant_name)s %(variant_name)s;'
436 )
437 Emit('};')
438 Emit('')
439
440 def _GenClassBegin(self, class_name, base_classes, depth):
441 # type: (str, List[str], int) -> None
442 if base_classes:
443 bases = ', '.join('public %s' % b for b in base_classes)
444 self.Emit("class %s : %s {" % (class_name, bases), depth)
445 else:
446 self.Emit("class %s {" % class_name, depth)
447 #self.Emit("class %s : public Walkable {" % class_name, depth)
448
449 self.Emit(" public:", depth)
450
451 def _GenClassEnd(self, class_name, depth):
452 # type: (str, int) -> None
453 self.Emit(' DISALLOW_COPY_AND_ASSIGN(%s)' % class_name)
454 self.Emit('};', depth)
455 self.Emit('', depth)
456
457 def _EmitMethodsInHeader(self, obj_header_str):
458 # type: (str) -> None
459 """Generate PrettyTree(), type_id(), obj_header()"""
460 if self.pretty_print_methods:
461 self.Emit(
462 'hnode_t* PrettyTree(bool do_abbrev, Dict<int, bool>* seen = nullptr);'
463 )
464 self.Emit('')
465
466 self.Emit('static constexpr ObjHeader obj_header() {')
467 self.Emit(' return %s;' % obj_header_str)
468 self.Emit('}')
469
470 def _GenClassForList(self, class_name, base_classes, tag_num):
471 # type: (str, List[str], int) -> None
472 depth = 0
473 self._GenClassBegin(class_name, base_classes, depth)
474
475 base_type_str = [b for b in base_classes if b.startswith('List<')][0]
476
477 # Zero arg constructor
478 self.Emit(' %s() : %s() {' % (class_name, base_type_str), depth)
479 self.Emit(' }', depth)
480
481 # One arg constructor used by Take.
482 # This should be is PROTECTED, like the superclass, but Alloc<T> calls
483 # it. Hm.
484 #self.Emit(' protected:')
485 self.Emit(
486 ' %s(%s* plain_list) : %s(plain_list) {' %
487 (class_name, base_type_str, base_type_str), depth)
488 self.Emit(' }', depth)
489
490 self.Emit(' static %s* New() {' % class_name, depth)
491 self.Emit(' return Alloc<%s>();' % class_name, depth)
492 self.Emit(' }', depth)
493
494 # Take() constructor
495 self.Emit(
496 ' static %s* Take(%s* plain_list) {' %
497 (class_name, base_type_str), depth)
498 self.Emit(' auto* result = Alloc<%s>(plain_list);' % class_name,
499 depth)
500 self.Emit(' plain_list->SetTaken();', depth)
501 self.Emit(' return result;', depth)
502 self.Emit(' }', depth)
503
504 # field_mask() should call List superclass, since say word_t won't have it
505 obj_header_str = 'ObjHeader::TaggedSubtype(%d, field_mask())' % tag_num
506 self.Indent()
507 self._EmitMethodsInHeader(obj_header_str)
508 self.Dedent()
509
510 self._GenClassEnd(class_name, depth)
511
512 def _GenClass(
513 self,
514 fields, # type: List[ast.Field]
515 class_name, # type: str
516 base_classes, # type: List[str]
517 depth, # type: int
518 tag_num, # type: Union[int, str]
519 obj_header_str='', # type: str
520 ):
521 # type: (...) -> None
522 """For Product and Constructor."""
523 self._GenClassBegin(class_name, base_classes, depth)
524
525 # Ensure that the member variables are ordered such that GC managed objects
526 # come before any unmanaged ones because we use `HeapTag::Scanned`.
527 managed_fields = []
528 unmanaged_fields = []
529 for f in fields:
530 if _IsManagedType(f.typ):
531 managed_fields.append(f)
532 else:
533 unmanaged_fields.append(f)
534 all_fields = managed_fields + unmanaged_fields
535
536 def FieldInitJoin(strs):
537 # type: (List[str]) -> str
538 # reflow doesn't work well here, so do it manually
539 return ',\n '.join(strs)
540
541 # Ensure that the constructor params are listed in the same order as the
542 # equivalent python constructors for compatibility in translated code.
543 params = []
544 for f in fields:
545 params.append('%s %s' % (_GetCppType(f.typ), f.name))
546
547 # Member initializers are in the same order as the member variables to
548 # avoid compiler warnings (the order doesn't affect the semantics).
549 inits = []
550 for f in all_fields:
551 inits.append('%s(%s)' % (f.name, f.name))
552
553 # Define constructor with N args
554 if len(inits):
555 self.Emit(' %s(%s)' % (class_name, ', '.join(params)), depth)
556 self.Emit(' : %s {' % FieldInitJoin(inits),
557 depth,
558 reflow=False)
559 self.Emit(' }')
560 else:
561 self.Emit(' %s(%s) {}' % (class_name, ', '.join(params)), depth)
562 self.Emit('')
563
564 # Define static constructor with ZERO args. Don't emit for types with no
565 # fields.
566 if fields:
567 init_args = []
568 for field in fields:
569 init_args.append(_DefaultValue(field.typ))
570
571 self.Emit(
572 ' static %s* CreateNull(bool alloc_lists = false) { ' %
573 class_name, depth)
574 self.Emit(
575 ' return Alloc<%s>(%s);' %
576 (class_name, ', '.join(init_args)), depth)
577 self.Emit(' }')
578 self.Emit('')
579
580 obj_header_str = 'ObjHeader::AsdlClass(%s, %d)' % (tag_num,
581 len(managed_fields))
582 self.Indent()
583 self._EmitMethodsInHeader(obj_header_str)
584 self.Dedent()
585
586 #
587 # Members
588 #
589 for field in all_fields:
590 self.Emit(" %s %s;" % (_GetCppType(field.typ), field.name))
591 self.Emit('')
592
593 self._GenClassEnd(class_name, depth)
594
595 def VisitSubType(self, subtype):
596 # type: (ast.SubTypeDecl) -> None
597 self._shared_type_tags[subtype.name] = self._product_counter
598
599 # Also create these last. They may inherit from sum types that have yet
600 # to be defined.
601 self._subtypes.append((subtype, self._product_counter))
602 self._product_counter += 1
603
604 def VisitProduct(self, product, name, depth):
605 # type: (ast.Product, str, int) -> None
606 self._shared_type_tags[name] = self._product_counter
607 # Create a tuple of _GenClass args to create LAST. They may inherit from
608 # sum types that have yet to be defined.
609 self._products.append((product, name, depth, self._product_counter))
610 self._product_counter += 1
611
612 def EmitFooter(self):
613 # type: () -> None
614 # Now generate all the product types we deferred.
615 for args in self._products:
616 ast_node, name, depth, tag_num = args
617 # Figure out base classes AFTERWARD.
618 bases = self._base_classes[name]
619 self._GenClass(ast_node.fields, name, bases, depth, tag_num)
620
621 for args in self._subtypes:
622 subtype, tag_num = args
623 # Figure out base classes AFTERWARD.
624 bases = self._base_classes[subtype.name]
625
626 cpp_type = _GetCppType(subtype.base_class)
627 assert cpp_type.endswith('*') # hack
628 cpp_type = cpp_type[:-1]
629 bases.append(cpp_type)
630
631 t = subtype.base_class.type_name
632 if t == 'List':
633 self._GenClassForList(subtype.name, bases, tag_num)
634
635 elif t == 'Dict':
636 raise AssertionError()
637 else:
638 #obj_header_str = ''
639 raise AssertionError()
640
641
642class MethodDefVisitor(visitor.AsdlVisitor):
643 """Generate the body of pretty printing methods.
644
645 We have to do this in another pass because types and schemas have
646 circular dependencies.
647 """
648
649 def __init__(self, f, abbrev_ns=None, abbrev_mod_entries=None):
650 # type: (IO, Optional[Any], List) -> None
651 visitor.AsdlVisitor.__init__(self, f)
652 self.abbrev_ns = abbrev_ns
653 self.abbrev_mod_entries = abbrev_mod_entries or []
654
655 def _EmitList(self, list_str, item_type, out_val_name):
656 # type: (str, ast.type_expr_t, str) -> None
657 # used in format strings
658 c_item_type = _GetCppType(item_type)
659
660 def _Emit(s):
661 # type: (str) -> None
662 self.Emit(s % sys._getframe(1).f_locals, self.current_depth)
663
664 _Emit(
665 'hnode::Array* %(out_val_name)s = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());'
666 )
667 _Emit(
668 'for (ListIter<%(c_item_type)s> it(%(list_str)s); !it.Done(); it.Next()) {'
669 )
670 _Emit(' %(c_item_type)s v_ = it.Value();')
671
672 child_code_str, none_guard = _HNodeExpr(item_type, 'v_')
673 if none_guard: # e.g. for List[Optional[value_t]]
674 # TODO: could consolidate this logic with asdl/runtime.py NewLeaf(), which is prebuilt/
675 child_code_str = (
676 '(v_ == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"), color_e::OtherConst) : %s'
677 % child_code_str)
678 _Emit(' hnode_t* h = %(child_code_str)s;')
679 _Emit(' %(out_val_name)s->children->append(h);')
680 _Emit('}')
681
682 def _EmitListPrettyPrint(self, field, out_val_name):
683 # type: (ast.Field, str) -> None
684 typ = field.typ
685 if ast.IsOptional(typ): # descend one level
686 typ = cast(ast.ParameterizedType, typ).children[0]
687 item_type = cast(ast.ParameterizedType, typ).children[0]
688
689 self._EmitList('this->%s' % field.name, item_type, out_val_name)
690
691 def _EmitDictPrettyPrint(self, field):
692 typ = field.typ
693 if typ.type_name == 'Optional': # descend one level
694 typ = typ.children[0]
695
696 k_typ = typ.children[0]
697 v_typ = typ.children[1]
698
699 k_c_type = _GetCppType(k_typ)
700 v_c_type = _GetCppType(v_typ)
701
702 k_code_str, _ = _HNodeExpr(k_typ, 'k')
703 v_code_str, _ = _HNodeExpr(v_typ, 'v')
704
705 self.Emit('auto* unnamed = NewList<hnode_t*>();')
706 self.Emit(
707 'auto* hdict = Alloc<hnode::Record>(kEmptyString, StrFromC("{"), StrFromC("}"), NewList<Field*>(), unnamed);'
708 )
709 self.Emit(
710 'for (DictIter<%s, %s> it(this->%s); !it.Done(); it.Next()) {' %
711 (k_c_type, v_c_type, field.name))
712 self.Emit(' auto k = it.Key();')
713 self.Emit(' auto v = it.Value();')
714 self.Emit(' unnamed->append(%s);' % k_code_str)
715 self.Emit(' unnamed->append(%s);' % v_code_str)
716 self.Emit('}')
717 self.Emit('L->append(Alloc<Field>(StrFromC("%s"), hdict));' %
718 field.name)
719
720 def _EmitCodeForField(self, field, counter):
721 # type: (ast.Field, int) -> None
722 """Generate code that returns an hnode for a field."""
723 out_val_name = 'x%d' % counter
724
725 if ast.IsList(field.typ):
726 self.Emit('if (this->%s != nullptr) { // List' % field.name)
727 self.Indent()
728 self._EmitListPrettyPrint(field, out_val_name)
729 self.Emit('L->append(Alloc<Field>(StrFromC("%s"), %s));' %
730 (field.name, out_val_name))
731 self.Dedent()
732 self.Emit('}')
733
734 elif ast.IsDict(field.typ):
735 self.Emit('if (this->%s != nullptr) { // Dict' % field.name)
736 self.Indent()
737 self._EmitDictPrettyPrint(field)
738 self.Dedent()
739 self.Emit('}')
740
741 elif ast.IsOptional(field.typ):
742 typ = cast(ast.ParameterizedType, field.typ).children[0]
743
744 self.Emit('if (this->%s) { // Optional' % field.name)
745 child_code_str, _ = _HNodeExpr(typ, 'this->%s' % field.name)
746 self.Emit(' hnode_t* %s = %s;' % (out_val_name, child_code_str))
747 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
748 (field.name, out_val_name))
749 self.Emit('}')
750
751 else:
752 var_name = 'this->%s' % field.name
753 code_str, obj_none_guard = _HNodeExpr(field.typ, var_name)
754
755 depth = self.current_depth
756 if obj_none_guard: # to satisfy MyPy type system
757 pass
758 self.Emit('hnode_t* %s = %s;' % (out_val_name, code_str), depth)
759
760 self.Emit(
761 'L->append(Alloc<Field>(StrFromC("%s"), %s));' %
762 (field.name, out_val_name), depth)
763
764 def _EmitPrettyPrintMethods(self,
765 class_name,
766 all_fields,
767 sum_name=None,
768 list_item_type=None):
769 # type: (str, List[ast.Field], Optional[str], Optional[Any]) -> None
770 """
771 """
772 self.Emit('')
773 self.Emit(
774 'hnode_t* %s::PrettyTree(bool do_abbrev, Dict<int, bool>* seen) {'
775 % class_name)
776
777 # Similar to j8::HeapValueId()
778 self.Emit(' seen = seen ? seen : Alloc<Dict<int, bool>>();')
779 self.Emit(' int heap_id = ObjectId(this);')
780 self.Emit(' if (dict_contains(seen, heap_id)) {')
781 self.Emit(' return Alloc<hnode::AlreadySeen>(heap_id);')
782 self.Emit(' }')
783 self.Emit(' seen->set(heap_id, true);')
784 self.Emit('')
785
786 if list_item_type:
787 self.Indent()
788 self._EmitList('this', list_item_type, 'out_node')
789 self.Dedent()
790 else:
791 if sum_name is not None:
792 n = '%s_str(this->tag())' % sum_name
793 else:
794 n = 'StrFromC("%s")' % class_name
795
796 abbrev_name = '_%s' % class_name
797
798 if abbrev_name in self.abbrev_mod_entries:
799 self.Emit(' if (do_abbrev) {')
800 self.Emit(' auto* p = %s::%s(this);' %
801 (self.abbrev_ns, abbrev_name))
802 self.Emit(' if (p) {')
803 self.Emit(' return p;')
804 self.Emit(' }')
805 self.Emit(' }')
806 else:
807 #self.Emit(' // no abbrev %s' % abbrev_name)
808 pass
809
810 self.Emit(' hnode::Record* out_node = runtime::NewRecord(%s);' %
811 n)
812 if all_fields:
813 self.Emit(' List<Field*>* L = out_node->fields;')
814 self.Emit('')
815
816 # Use the runtime type to be more like asdl/format.py
817 for local_id, field in enumerate(all_fields):
818 #log('%s :: %s', field_name, field_desc)
819 self.Indent()
820 self._EmitCodeForField(field, local_id)
821 self.Dedent()
822 self.Emit('')
823 self.Emit(' return out_node;')
824 self.Emit('}')
825 self.Emit('')
826
827 def _EmitStrFunction(self,
828 sum,
829 sum_name,
830 depth,
831 strong=False,
832 simple=False):
833 # type: (ast.Sum, str, int, bool, bool) -> None
834 add_suffix = not ('no_namespace_suffix' in sum.generate)
835 if add_suffix:
836 if simple:
837 enum_name = '%s_i' % sum_name
838 else:
839 enum_name = '%s_e' % sum_name
840 else:
841 enum_name = sum_name
842
843 if strong:
844 self.Emit(
845 'BigStr* %s_str(%s tag, bool dot) {' % (sum_name, enum_name),
846 depth)
847 else:
848 self.Emit('BigStr* %s_str(int tag, bool dot) {' % sum_name, depth)
849
850 buf_size = 32
851 v_max = max(len(variant.name) for variant in sum.types)
852 s_max = v_max + 1 + len(sum_name) + 1 # for . and NUL
853 if s_max > buf_size:
854 raise RuntimeError('Sum name %r + variant name is too long' %
855 sum_name)
856
857 self.Emit(' char buf[%d];' % buf_size, depth)
858 self.Emit(' const char* v = nullptr;', depth)
859 self.Emit(' switch (tag) {', depth)
860 for variant in sum.types:
861 self.Emit('case %s::%s:' % (enum_name, variant.name), depth + 1)
862 self.Emit(' v = "%s"; break;' % variant.name, depth + 1)
863
864 self.Emit('default:', depth + 1)
865 self.Emit(' assert(0);', depth + 1)
866
867 self.Emit(' }', depth)
868 self.Emit(' if (dot) {', depth)
869 self.Emit(' snprintf(buf, %d, "%s.%%s", v);' % (buf_size, sum_name),
870 depth)
871 self.Emit(' return StrFromC(buf);', depth)
872 self.Emit(' } else {', depth)
873 self.Emit(' return StrFromC(v);', depth)
874 self.Emit(' }', depth)
875 self.Emit('}', depth)
876
877 def VisitSimpleSum(self, sum, name, depth):
878 # type: (ast.SimpleSum, str, int) -> None
879 if 'integers' in sum.generate or 'uint16' in sum.generate:
880 self._EmitStrFunction(sum, name, depth, strong=False, simple=True)
881 else:
882 self._EmitStrFunction(sum, name, depth, strong=True)
883
884 def VisitCompoundSum(self, sum, sum_name, depth):
885 # type: (ast.Sum, str, int) -> None
886 self._EmitStrFunction(sum, sum_name, depth)
887
888 # Generate definitions for the for zero arg singleton globals
889 for variant in sum.types:
890 if variant.shared_type:
891 continue
892 if len(variant.fields) == 0:
893 variant_name = variant.name
894 self.Emit('')
895 self.Emit('%s__%s* %s::%s = &g%s__%s.obj;' %
896 (sum_name, variant_name, sum_name, variant_name,
897 sum_name, variant_name))
898 self.Emit('')
899 self.Emit('GcGlobal<%s__%s> g%s__%s = ' %
900 (sum_name, variant_name, sum_name, variant_name))
901 self.Emit(' { ObjHeader::Global(%s_e::%s) };' %
902 (sum_name, variant_name))
903
904 for variant in sum.types:
905 if variant.shared_type:
906 continue
907 all_fields = variant.fields
908 class_name = '%s__%s' % (sum_name, variant.name)
909 self._EmitPrettyPrintMethods(class_name,
910 all_fields,
911 sum_name=sum_name)
912
913 # Emit dispatch WITHOUT using 'virtual'
914 self.Emit('')
915 self.Emit(
916 'hnode_t* %s_t::PrettyTree(bool do_abbrev, Dict<int, bool>* seen) {'
917 % sum_name)
918 self.Emit(' switch (this->tag()) {', depth)
919
920 for variant in sum.types:
921 if variant.shared_type:
922 subtype_name = variant.shared_type
923 else:
924 subtype_name = '%s__%s' % (sum_name, variant.name)
925
926 self.Emit(' case %s_e::%s: {' % (sum_name, variant.name), depth)
927 self.Emit(
928 ' %s* obj = static_cast<%s*>(this);' %
929 (subtype_name, subtype_name), depth)
930 self.Emit(' return obj->PrettyTree(do_abbrev, seen);', depth)
931 self.Emit(' }', depth)
932
933 self.Emit(' default:', depth)
934 self.Emit(' assert(0);', depth)
935
936 self.Emit(' }')
937 self.Emit('}')
938
939 def VisitProduct(self, product, name, depth):
940 # type: (ast.Product, str, int) -> None
941 self._EmitPrettyPrintMethods(name, product.fields)
942
943 def VisitSubType(self, subtype):
944 # type: (ast.SubTypeDecl) -> None
945 list_item_type = None
946 b = subtype.base_class
947 if isinstance(b, ast.ParameterizedType):
948 if b.type_name == 'List':
949 list_item_type = b.children[0]
950 self._EmitPrettyPrintMethods(subtype.name, [],
951 list_item_type=list_item_type)