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

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