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

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