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

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