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

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