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

877 lines, 589 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 _EmitList(self, abbrev, list_str, item_type, out_val_name):
557 # used in format strings
558 c_item_type = _GetCppType(item_type)
559
560 def _Emit(s):
561 self.Emit(s % sys._getframe(1).f_locals, self.current_depth)
562
563 _Emit(
564 'hnode::Array* %(out_val_name)s = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());'
565 )
566 _Emit(
567 'for (ListIter<%(c_item_type)s> it(%(list_str)s); !it.Done(); it.Next()) {'
568 )
569 _Emit(' %(c_item_type)s v_ = it.Value();')
570
571 child_code_str, none_guard = _HNodeExpr(abbrev, item_type, 'v_')
572 if none_guard: # e.g. for List[Optional[value_t]]
573 # TODO: could consolidate this logic with asdl/runtime.py NewLeaf(), which is prebuilt/
574 child_code_str = (
575 '(v_ == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"), color_e::OtherConst) : %s'
576 % child_code_str)
577 _Emit(' hnode_t* h = %(child_code_str)s;')
578 _Emit(' %(out_val_name)s->children->append(h);')
579 _Emit('}')
580
581 def _EmitListPrettyPrint(self, abbrev, field, out_val_name):
582 typ = field.typ
583 if typ.type_name == 'Optional': # descend one level
584 typ = typ.children[0]
585 item_type = typ.children[0]
586
587 self._EmitList(abbrev, 'this->%s' % field.name, item_type,
588 out_val_name)
589
590 def _EmitDictPrettyPrint(self, abbrev, field, out_val_name, counter):
591 k = 'k%d' % counter
592 v = 'v%d' % counter
593
594 typ = field.typ
595 if typ.type_name == 'Optional': # descend one level
596 typ = typ.children[0]
597
598 k_typ = typ.children[0]
599 v_typ = typ.children[1]
600
601 k_c_type = _GetCppType(k_typ)
602 v_c_type = _GetCppType(v_typ)
603
604 k_code_str, _ = _HNodeExpr(abbrev, k_typ, k)
605 v_code_str, _ = _HNodeExpr(abbrev, v_typ, v)
606
607 self.Emit('if (this->%s) { // Dict' % field.name)
608 # TODO: m can be a global constant!
609 self.Emit(
610 ' auto m = Alloc<hnode::Leaf>(StrFromC("Dict"), color_e::OtherConst);'
611 )
612 self.Emit(
613 ' hnode::Array* %s = Alloc<hnode::Array>(NewList<hnode_t*>({m}));'
614 % out_val_name)
615 self.Emit(
616 ' for (DictIter<%s, %s> it(this->%s); !it.Done(); it.Next()) {' %
617 (k_c_type, v_c_type, field.name))
618 self.Emit(' auto %s = it.Key();' % k)
619 self.Emit(' auto %s = it.Value();' % v)
620 self.Emit(' %s->children->append(%s);' % (out_val_name, k_code_str))
621 self.Emit(' %s->children->append(%s);' % (out_val_name, v_code_str))
622 self.Emit(' }')
623 self.Emit(' L->append(Alloc<Field>(StrFromC ("%s"), %s));' %
624 (field.name, out_val_name))
625 self.Emit('}')
626
627 def _EmitCodeForField(self, abbrev, field, counter):
628 """Generate code that returns an hnode for a field."""
629 out_val_name = 'x%d' % counter
630
631 if field.typ.IsList():
632 self.Emit('if (this->%s != nullptr) { // List' % field.name)
633 self.Indent()
634 self._EmitListPrettyPrint(abbrev, field, out_val_name)
635 self.Emit('L->append(Alloc<Field>(StrFromC("%s"), %s));' %
636 (field.name, out_val_name))
637 self.Dedent()
638 self.Emit('}')
639
640 elif field.typ.IsDict():
641 self._EmitDictPrettyPrint(abbrev, field, out_val_name, counter)
642
643 elif field.typ.IsOptional():
644 typ = field.typ.children[0]
645
646 self.Emit('if (this->%s) { // Optional' % field.name)
647 child_code_str, _ = _HNodeExpr(abbrev, typ,
648 'this->%s' % field.name)
649 self.Emit(' hnode_t* %s = %s;' % (out_val_name, child_code_str))
650 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
651 (field.name, out_val_name))
652 self.Emit('}')
653
654 else:
655 var_name = 'this->%s' % field.name
656 code_str, obj_none_guard = _HNodeExpr(abbrev, field.typ, var_name)
657
658 depth = self.current_depth
659 if obj_none_guard: # to satisfy MyPy type system
660 pass
661 self.Emit('hnode_t* %s = %s;' % (out_val_name, code_str), depth)
662
663 self.Emit(
664 'L->append(Alloc<Field>(StrFromC("%s"), %s));' %
665 (field.name, out_val_name), depth)
666
667 def _EmitPrettyPrintMethods(self,
668 class_name,
669 all_fields,
670 sum_name=None,
671 list_item_type=None):
672
673 #
674 # PrettyTree
675 #
676
677 if sum_name is not None:
678 n = '%s_str(this->tag())' % sum_name
679 else:
680 n = 'StrFromC("%s")' % class_name
681
682 self.Emit('')
683 self.Emit('hnode_t* %s::PrettyTree(Dict<int, bool>* seen) {' %
684 class_name)
685
686 # Similar to j8::HeapValueId()
687 self.Emit(' seen = seen ? seen : Alloc<Dict<int, bool>>();')
688 self.Emit(' int heap_id = ObjectId(this);')
689 self.Emit(' if (dict_contains(seen, heap_id)) {')
690 self.Emit(' return Alloc<hnode::AlreadySeen>(heap_id);')
691 self.Emit(' }')
692 self.Emit(' seen->set(heap_id, true);')
693 self.Emit('')
694
695 if list_item_type:
696 self.Indent()
697 self._EmitList('PrettyTree', 'this', list_item_type, 'out_node')
698 self.Dedent()
699 else:
700 self.Emit(' hnode::Record* out_node = runtime::NewRecord(%s);' %
701 n)
702 if all_fields:
703 self.Emit(' List<Field*>* L = out_node->fields;')
704 self.Emit('')
705
706 # Use the runtime type to be more like asdl/format.py
707 for local_id, field in enumerate(all_fields):
708 #log('%s :: %s', field_name, field_desc)
709 self.Indent()
710 self._EmitCodeForField('PrettyTree', field, local_id)
711 self.Dedent()
712 self.Emit('')
713 self.Emit(' return out_node;')
714 self.Emit('}')
715 self.Emit('')
716
717 #
718 # _AbbreviatedTree
719 #
720
721 if not ABBREV:
722 return
723
724 self.Emit('')
725 self.Emit('hnode_t* %s::_AbbreviatedTree() {' % class_name)
726 if is_list:
727 raise AssertionError()
728 else:
729 self.Emit(' hnode::Record* out_node = runtime::NewRecord("%s");' %
730 n)
731 if all_fields:
732 self.Emit(' List<Field*>* L = out_node->fields;')
733
734 for local_id, field in enumerate(all_fields):
735 self.Indent()
736 self._EmitCodeForField('AbbreviatedTree', field, local_id)
737 self.Dedent()
738 self.Emit('')
739 self.Emit(' return out_node;')
740 self.Emit('}')
741 self.Emit('')
742
743 self.Emit('hnode_t* %s::AbbreviatedTree() {' % class_name)
744 abbrev_name = '_%s' % class_name
745
746 # STUB
747 self.abbrev_mod_entries = []
748
749 if abbrev_name in self.abbrev_mod_entries:
750 self.Emit(' hnode_t* p = %s();' % abbrev_name)
751 # If the user function didn't return anything, fall back.
752 self.Emit(' return p ? p : _AbbreviatedTree();')
753 else:
754 self.Emit(' return _AbbreviatedTree();')
755 self.Emit('}')
756
757 def _EmitStrFunction(self,
758 sum,
759 sum_name,
760 depth,
761 strong=False,
762 simple=False):
763 add_suffix = not ('no_namespace_suffix' in sum.generate)
764 if add_suffix:
765 if simple:
766 enum_name = '%s_i' % sum_name
767 else:
768 enum_name = '%s_e' % sum_name
769 else:
770 enum_name = sum_name
771
772 if strong:
773 self.Emit(
774 'BigStr* %s_str(%s tag, bool dot) {' % (sum_name, enum_name),
775 depth)
776 else:
777 self.Emit('BigStr* %s_str(int tag, bool dot) {' % sum_name, depth)
778
779 buf_size = 32
780 v_max = max(len(variant.name) for variant in sum.types)
781 s_max = v_max + 1 + len(sum_name) + 1 # for . and NUL
782 if s_max > buf_size:
783 raise RuntimeError('Sum name %r + variant name is too long' %
784 sum_name)
785
786 self.Emit(' char buf[%d];' % buf_size, depth)
787 self.Emit(' const char* v = nullptr;', depth)
788 self.Emit(' switch (tag) {', depth)
789 for variant in sum.types:
790 self.Emit('case %s::%s:' % (enum_name, variant.name), depth + 1)
791 self.Emit(' v = "%s"; break;' % variant.name, depth + 1)
792
793 self.Emit('default:', depth + 1)
794 self.Emit(' assert(0);', depth + 1)
795
796 self.Emit(' }', depth)
797 self.Emit(' if (dot) {', depth)
798 self.Emit(' snprintf(buf, %d, "%s.%%s", v);' % (buf_size, sum_name),
799 depth)
800 self.Emit(' return StrFromC(buf);', depth)
801 self.Emit(' } else {', depth)
802 self.Emit(' return StrFromC(v);', depth)
803 self.Emit(' }', depth)
804 self.Emit('}', depth)
805
806 def VisitSimpleSum(self, sum, name, depth):
807 if 'integers' in sum.generate or 'uint16' in sum.generate:
808 self._EmitStrFunction(sum, name, depth, strong=False, simple=True)
809 else:
810 self._EmitStrFunction(sum, name, depth, strong=True)
811
812 def VisitCompoundSum(self, sum, sum_name, depth):
813 self._EmitStrFunction(sum, sum_name, depth)
814
815 # Generate definitions for the for zero arg singleton globals
816 for variant in sum.types:
817 if variant.shared_type:
818 continue
819 if len(variant.fields) == 0:
820 variant_name = variant.name
821 self.Emit('')
822 self.Emit('%s__%s* %s::%s = &g%s__%s.obj;' %
823 (sum_name, variant_name, sum_name, variant_name,
824 sum_name, variant_name))
825 self.Emit('')
826 self.Emit('GcGlobal<%s__%s> g%s__%s = ' %
827 (sum_name, variant_name, sum_name, variant_name))
828 self.Emit(' { ObjHeader::Global(%s_e::%s) };' %
829 (sum_name, variant_name))
830
831 for variant in sum.types:
832 if variant.shared_type:
833 continue
834 all_fields = variant.fields
835 class_name = '%s__%s' % (sum_name, variant.name)
836 self._EmitPrettyPrintMethods(class_name,
837 all_fields,
838 sum_name=sum_name)
839
840 # Emit dispatch WITHOUT using 'virtual'
841 for func_name in PRETTY_METHODS:
842 self.Emit('')
843 self.Emit('hnode_t* %s_t::%s(Dict<int, bool>* seen) {' %
844 (sum_name, func_name))
845 self.Emit(' switch (this->tag()) {', depth)
846
847 for variant in sum.types:
848 if variant.shared_type:
849 subtype_name = variant.shared_type
850 else:
851 subtype_name = '%s__%s' % (sum_name, variant.name)
852
853 self.Emit(' case %s_e::%s: {' % (sum_name, variant.name),
854 depth)
855 self.Emit(
856 ' %s* obj = static_cast<%s*>(this);' %
857 (subtype_name, subtype_name), depth)
858 self.Emit(' return obj->%s(seen);' % func_name, depth)
859 self.Emit(' }', depth)
860
861 self.Emit(' default:', depth)
862 self.Emit(' assert(0);', depth)
863
864 self.Emit(' }')
865 self.Emit('}')
866
867 def VisitProduct(self, product, name, depth):
868 self._EmitPrettyPrintMethods(name, product.fields)
869
870 def VisitSubType(self, subtype):
871 list_item_type = None
872 b = subtype.base_class
873 if isinstance(b, ast.ParameterizedType):
874 if b.type_name == 'List':
875 list_item_type = b.children[0]
876 self._EmitPrettyPrintMethods(subtype.name, [],
877 list_item_type=list_item_type)