OILS / asdl / gen_cpp.py View on Github | oilshell.org

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