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

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