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

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