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

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