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

900 lines, 611 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,
423 fields,
424 class_name,
425 base_classes,
426 depth,
427 tag,
428 obj_header_str=''):
429 """For Product and Constructor."""
430 if base_classes:
431 bases = ', '.join('public %s' % b for b in base_classes)
432 self.Emit("class %s : %s {" % (class_name, bases), depth)
433 else:
434 self.Emit("class %s {" % class_name, depth)
435 self.Emit(" public:", depth)
436
437 # Ensure that the member variables are ordered such that GC managed objects
438 # come before any unmanaged ones because we use `HeapTag::Scanned`.
439 managed_fields = []
440 unmanaged_fields = []
441 for f in fields:
442 if _IsManagedType(f.typ):
443 managed_fields.append(f)
444 else:
445 unmanaged_fields.append(f)
446 all_fields = managed_fields + unmanaged_fields
447
448 def FieldInitJoin(strs):
449 # reflow doesn't work well here, so do it manually
450 return ',\n '.join(strs)
451
452 # Ensure that the constructor params are listed in the same order as the
453 # equivalent python constructors for compatibility in translated code.
454 params = []
455 for f in fields:
456 params.append('%s %s' % (_GetCppType(f.typ), f.name))
457
458 # Member initializers are in the same order as the member variables to
459 # avoid compiler warnings (the order doesn't affect the semantics).
460 inits = []
461 for f in all_fields:
462 inits.append('%s(%s)' % (f.name, f.name))
463
464 # Define constructor with N args
465 if len(inits):
466 self.Emit(' %s(%s)' % (class_name, ', '.join(params)), depth)
467 self.Emit(' : %s {' % FieldInitJoin(inits),
468 depth,
469 reflow=False)
470 self.Emit(' }')
471 else:
472 self.Emit(' %s(%s) {}' % (class_name, ', '.join(params)), depth)
473 self.Emit('')
474
475 # Define static constructor with ZERO args. Don't emit for types with no
476 # fields.
477 if fields:
478 init_args = []
479 for field in fields:
480 init_args.append(_DefaultValue(field.typ))
481
482 self.Emit(
483 ' static %s* CreateNull(bool alloc_lists = false) { ' %
484 class_name, depth)
485 self.Emit(
486 ' return Alloc<%s>(%s);' %
487 (class_name, ', '.join(init_args)), depth)
488 self.Emit(' }')
489 self.Emit('')
490
491 if self.pretty_print_methods:
492 for abbrev in PRETTY_METHODS:
493 self.Emit(
494 ' hnode_t* %s(Dict<int, bool>* seen = nullptr);' % abbrev,
495 depth)
496 self.Emit('')
497
498 if not obj_header_str: # override for subtypes
499 obj_header_str = 'ObjHeader::AsdlClass(%s, %d)' % (
500 tag, len(managed_fields))
501
502 self.Emit(' static constexpr ObjHeader obj_header() {')
503 self.Emit(' return %s;' % obj_header_str)
504 self.Emit(' }')
505 self.Emit('')
506
507 #
508 # Members
509 #
510 for field in all_fields:
511 self.Emit(" %s %s;" % (_GetCppType(field.typ), field.name))
512
513 self.Emit('')
514 self.Emit(' DISALLOW_COPY_AND_ASSIGN(%s)' % class_name)
515 self.Emit('};', depth)
516 self.Emit('', depth)
517
518 def VisitSubType(self, subtype):
519 self._shared_type_tags[subtype.name] = self._product_counter
520
521 # Also create these last. They may inherit from sum types that have yet
522 # to be defined.
523 self._subtypes.append((subtype, self._product_counter))
524 self._product_counter += 1
525
526 def VisitProduct(self, product, name, depth):
527 self._shared_type_tags[name] = self._product_counter
528 # Create a tuple of _GenClass args to create LAST. They may inherit from
529 # sum types that have yet to be defined.
530 self._products.append((product, name, depth, self._product_counter))
531 self._product_counter += 1
532
533 def EmitFooter(self):
534 # Now generate all the product types we deferred.
535 for args in self._products:
536 ast_node, name, depth, tag_num = args
537 # Figure out base classes AFTERWARD.
538 bases = self._base_classes[name]
539 self._GenClass(ast_node.fields, name, bases, depth, tag_num)
540
541 for args in self._subtypes:
542 subtype, tag_num = args
543 # Figure out base classes AFTERWARD.
544 bases = self._base_classes[subtype.name]
545
546 cpp_type = _GetCppType(subtype.base_class)
547 assert cpp_type.endswith('*') # hack
548 cpp_type = cpp_type[:-1]
549 bases.append(cpp_type)
550
551 t = subtype.base_class.type_name
552 if t == 'List':
553 # field_mask() should call List superclass, since say word_t won't have it
554 obj_header_str = 'ObjHeader::TaggedSubtype(%d, field_mask())' % tag_num
555
556 elif t == 'Dict':
557 raise AssertionError()
558 else:
559 obj_header_str = ''
560
561 self._GenClass([],
562 subtype.name,
563 bases,
564 0,
565 tag_num,
566 obj_header_str=obj_header_str)
567
568
569class MethodDefVisitor(visitor.AsdlVisitor):
570 """Generate the body of pretty printing methods.
571
572 We have to do this in another pass because types and schemas have
573 circular dependencies.
574 """
575
576 def __init__(self, f, pretty_print_methods=True):
577 visitor.AsdlVisitor.__init__(self, f)
578
579 def _EmitList(self, abbrev, list_str, item_type, out_val_name):
580 # used in format strings
581 c_item_type = _GetCppType(item_type)
582
583 def _Emit(s):
584 self.Emit(s % sys._getframe(1).f_locals, self.current_depth)
585
586 _Emit(
587 'hnode::Array* %(out_val_name)s = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());'
588 )
589 _Emit(
590 'for (ListIter<%(c_item_type)s> it(%(list_str)s); !it.Done(); it.Next()) {'
591 )
592 _Emit(' %(c_item_type)s v_ = it.Value();')
593
594 child_code_str, none_guard = _HNodeExpr(abbrev, item_type, 'v_')
595 if none_guard: # e.g. for List[Optional[value_t]]
596 # TODO: could consolidate this logic with asdl/runtime.py NewLeaf(), which is prebuilt/
597 child_code_str = (
598 '(v_ == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"), color_e::OtherConst) : %s'
599 % child_code_str)
600 _Emit(' hnode_t* h = %(child_code_str)s;')
601 _Emit(' %(out_val_name)s->children->append(h);')
602 _Emit('}')
603
604 def _EmitListPrettyPrint(self, abbrev, field, out_val_name):
605 typ = field.typ
606 if typ.type_name == 'Optional': # descend one level
607 typ = typ.children[0]
608 item_type = typ.children[0]
609
610 self._EmitList(abbrev, 'this->%s' % field.name, item_type,
611 out_val_name)
612
613 def _EmitDictPrettyPrint(self, abbrev, field, out_val_name, counter):
614 k = 'k%d' % counter
615 v = 'v%d' % counter
616
617 typ = field.typ
618 if typ.type_name == 'Optional': # descend one level
619 typ = typ.children[0]
620
621 k_typ = typ.children[0]
622 v_typ = typ.children[1]
623
624 k_c_type = _GetCppType(k_typ)
625 v_c_type = _GetCppType(v_typ)
626
627 k_code_str, _ = _HNodeExpr(abbrev, k_typ, k)
628 v_code_str, _ = _HNodeExpr(abbrev, v_typ, v)
629
630 self.Emit('if (this->%s) { // Dict' % field.name)
631 # TODO: m can be a global constant!
632 self.Emit(
633 ' auto m = Alloc<hnode::Leaf>(StrFromC("Dict"), color_e::OtherConst);'
634 )
635 self.Emit(
636 ' hnode::Array* %s = Alloc<hnode::Array>(NewList<hnode_t*>({m}));'
637 % out_val_name)
638 self.Emit(
639 ' for (DictIter<%s, %s> it(this->%s); !it.Done(); it.Next()) {' %
640 (k_c_type, v_c_type, field.name))
641 self.Emit(' auto %s = it.Key();' % k)
642 self.Emit(' auto %s = it.Value();' % v)
643 self.Emit(' %s->children->append(%s);' % (out_val_name, k_code_str))
644 self.Emit(' %s->children->append(%s);' % (out_val_name, v_code_str))
645 self.Emit(' }')
646 self.Emit(' L->append(Alloc<Field>(StrFromC ("%s"), %s));' %
647 (field.name, out_val_name))
648 self.Emit('}')
649
650 def _EmitCodeForField(self, abbrev, field, counter):
651 """Generate code that returns an hnode for a field."""
652 out_val_name = 'x%d' % counter
653
654 if field.typ.IsList():
655 self.Emit('if (this->%s != nullptr) { // List' % field.name)
656 self.Indent()
657 self._EmitListPrettyPrint(abbrev, field, out_val_name)
658 self.Emit('L->append(Alloc<Field>(StrFromC("%s"), %s));' %
659 (field.name, out_val_name))
660 self.Dedent()
661 self.Emit('}')
662
663 elif field.typ.IsDict():
664 self._EmitDictPrettyPrint(abbrev, field, out_val_name, counter)
665
666 elif field.typ.IsOptional():
667 typ = field.typ.children[0]
668
669 self.Emit('if (this->%s) { // Optional' % field.name)
670 child_code_str, _ = _HNodeExpr(abbrev, typ,
671 'this->%s' % field.name)
672 self.Emit(' hnode_t* %s = %s;' % (out_val_name, child_code_str))
673 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
674 (field.name, out_val_name))
675 self.Emit('}')
676
677 else:
678 var_name = 'this->%s' % field.name
679 code_str, obj_none_guard = _HNodeExpr(abbrev, field.typ, var_name)
680
681 depth = self.current_depth
682 if obj_none_guard: # to satisfy MyPy type system
683 pass
684 self.Emit('hnode_t* %s = %s;' % (out_val_name, code_str), depth)
685
686 self.Emit(
687 'L->append(Alloc<Field>(StrFromC("%s"), %s));' %
688 (field.name, out_val_name), depth)
689
690 def _EmitPrettyPrintMethods(self,
691 class_name,
692 all_fields,
693 sum_name=None,
694 list_item_type=None):
695
696 #
697 # PrettyTree
698 #
699
700 if sum_name is not None:
701 n = '%s_str(this->tag())' % sum_name
702 else:
703 n = 'StrFromC("%s")' % class_name
704
705 self.Emit('')
706 self.Emit('hnode_t* %s::PrettyTree(Dict<int, bool>* seen) {' %
707 class_name)
708
709 # Similar to j8::HeapValueId()
710 self.Emit(' seen = seen ? seen : Alloc<Dict<int, bool>>();')
711 self.Emit(' int heap_id = ObjectId(this);')
712 self.Emit(' if (dict_contains(seen, heap_id)) {')
713 self.Emit(' return Alloc<hnode::AlreadySeen>(heap_id);')
714 self.Emit(' }')
715 self.Emit(' seen->set(heap_id, true);')
716 self.Emit('')
717
718 if list_item_type:
719 self.Indent()
720 self._EmitList('PrettyTree', 'this', list_item_type, 'out_node')
721 self.Dedent()
722 else:
723 self.Emit(' hnode::Record* out_node = runtime::NewRecord(%s);' %
724 n)
725 if all_fields:
726 self.Emit(' List<Field*>* L = out_node->fields;')
727 self.Emit('')
728
729 # Use the runtime type to be more like asdl/format.py
730 for local_id, field in enumerate(all_fields):
731 #log('%s :: %s', field_name, field_desc)
732 self.Indent()
733 self._EmitCodeForField('PrettyTree', field, local_id)
734 self.Dedent()
735 self.Emit('')
736 self.Emit(' return out_node;')
737 self.Emit('}')
738 self.Emit('')
739
740 #
741 # _AbbreviatedTree
742 #
743
744 if not ABBREV:
745 return
746
747 self.Emit('')
748 self.Emit('hnode_t* %s::_AbbreviatedTree() {' % class_name)
749 if list_item_type:
750 raise AssertionError()
751 else:
752 self.Emit(' hnode::Record* out_node = runtime::NewRecord("%s");' %
753 n)
754 if all_fields:
755 self.Emit(' List<Field*>* L = out_node->fields;')
756
757 for local_id, field in enumerate(all_fields):
758 self.Indent()
759 self._EmitCodeForField('AbbreviatedTree', field, local_id)
760 self.Dedent()
761 self.Emit('')
762 self.Emit(' return out_node;')
763 self.Emit('}')
764 self.Emit('')
765
766 self.Emit('hnode_t* %s::AbbreviatedTree() {' % class_name)
767 abbrev_name = '_%s' % class_name
768
769 # STUB
770 self.abbrev_mod_entries = []
771
772 if abbrev_name in self.abbrev_mod_entries:
773 self.Emit(' hnode_t* p = %s();' % abbrev_name)
774 # If the user function didn't return anything, fall back.
775 self.Emit(' return p ? p : _AbbreviatedTree();')
776 else:
777 self.Emit(' return _AbbreviatedTree();')
778 self.Emit('}')
779
780 def _EmitStrFunction(self,
781 sum,
782 sum_name,
783 depth,
784 strong=False,
785 simple=False):
786 add_suffix = not ('no_namespace_suffix' in sum.generate)
787 if add_suffix:
788 if simple:
789 enum_name = '%s_i' % sum_name
790 else:
791 enum_name = '%s_e' % sum_name
792 else:
793 enum_name = sum_name
794
795 if strong:
796 self.Emit(
797 'BigStr* %s_str(%s tag, bool dot) {' % (sum_name, enum_name),
798 depth)
799 else:
800 self.Emit('BigStr* %s_str(int tag, bool dot) {' % sum_name, depth)
801
802 buf_size = 32
803 v_max = max(len(variant.name) for variant in sum.types)
804 s_max = v_max + 1 + len(sum_name) + 1 # for . and NUL
805 if s_max > buf_size:
806 raise RuntimeError('Sum name %r + variant name is too long' %
807 sum_name)
808
809 self.Emit(' char buf[%d];' % buf_size, depth)
810 self.Emit(' const char* v = nullptr;', depth)
811 self.Emit(' switch (tag) {', depth)
812 for variant in sum.types:
813 self.Emit('case %s::%s:' % (enum_name, variant.name), depth + 1)
814 self.Emit(' v = "%s"; break;' % variant.name, depth + 1)
815
816 self.Emit('default:', depth + 1)
817 self.Emit(' assert(0);', depth + 1)
818
819 self.Emit(' }', depth)
820 self.Emit(' if (dot) {', depth)
821 self.Emit(' snprintf(buf, %d, "%s.%%s", v);' % (buf_size, sum_name),
822 depth)
823 self.Emit(' return StrFromC(buf);', depth)
824 self.Emit(' } else {', depth)
825 self.Emit(' return StrFromC(v);', depth)
826 self.Emit(' }', depth)
827 self.Emit('}', depth)
828
829 def VisitSimpleSum(self, sum, name, depth):
830 if 'integers' in sum.generate or 'uint16' in sum.generate:
831 self._EmitStrFunction(sum, name, depth, strong=False, simple=True)
832 else:
833 self._EmitStrFunction(sum, name, depth, strong=True)
834
835 def VisitCompoundSum(self, sum, sum_name, depth):
836 self._EmitStrFunction(sum, sum_name, depth)
837
838 # Generate definitions for the for zero arg singleton globals
839 for variant in sum.types:
840 if variant.shared_type:
841 continue
842 if len(variant.fields) == 0:
843 variant_name = variant.name
844 self.Emit('')
845 self.Emit('%s__%s* %s::%s = &g%s__%s.obj;' %
846 (sum_name, variant_name, sum_name, variant_name,
847 sum_name, variant_name))
848 self.Emit('')
849 self.Emit('GcGlobal<%s__%s> g%s__%s = ' %
850 (sum_name, variant_name, sum_name, variant_name))
851 self.Emit(' { ObjHeader::Global(%s_e::%s) };' %
852 (sum_name, variant_name))
853
854 for variant in sum.types:
855 if variant.shared_type:
856 continue
857 all_fields = variant.fields
858 class_name = '%s__%s' % (sum_name, variant.name)
859 self._EmitPrettyPrintMethods(class_name,
860 all_fields,
861 sum_name=sum_name)
862
863 # Emit dispatch WITHOUT using 'virtual'
864 for func_name in PRETTY_METHODS:
865 self.Emit('')
866 self.Emit('hnode_t* %s_t::%s(Dict<int, bool>* seen) {' %
867 (sum_name, func_name))
868 self.Emit(' switch (this->tag()) {', depth)
869
870 for variant in sum.types:
871 if variant.shared_type:
872 subtype_name = variant.shared_type
873 else:
874 subtype_name = '%s__%s' % (sum_name, variant.name)
875
876 self.Emit(' case %s_e::%s: {' % (sum_name, variant.name),
877 depth)
878 self.Emit(
879 ' %s* obj = static_cast<%s*>(this);' %
880 (subtype_name, subtype_name), depth)
881 self.Emit(' return obj->%s(seen);' % func_name, depth)
882 self.Emit(' }', depth)
883
884 self.Emit(' default:', depth)
885 self.Emit(' assert(0);', depth)
886
887 self.Emit(' }')
888 self.Emit('}')
889
890 def VisitProduct(self, product, name, depth):
891 self._EmitPrettyPrintMethods(name, product.fields)
892
893 def VisitSubType(self, subtype):
894 list_item_type = None
895 b = subtype.base_class
896 if isinstance(b, ast.ParameterizedType):
897 if b.type_name == 'List':
898 list_item_type = b.children[0]
899 self._EmitPrettyPrintMethods(subtype.name, [],
900 list_item_type=list_item_type)