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

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