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

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