OILS / asdl / gen_cpp.py View on Github | oilshell.org

848 lines, 563 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# Not supporting concise syntax tree like Python
34ABBREV = False
35
36if ABBREV:
37 PRETTY_METHODS = ['PrettyTree', '_AbbreviatedTree', 'AbbreviatedTree']
38else:
39 PRETTY_METHODS = ['PrettyTree']
40
41
42# Used by core/asdl_gen.py to generate _devbuild/gen/osh-types.h, with
43# lex_mode__*
44class CEnumVisitor(visitor.AsdlVisitor):
45
46 def VisitSimpleSum(self, sum, name, depth):
47 # Just use #define, since enums aren't namespaced.
48 for i, variant in enumerate(sum.types):
49 self.Emit('#define %s__%s %d' % (name, variant.name, i + 1), depth)
50 self.Emit("", depth)
51
52
53_PRIMITIVES = {
54 'string': 'BigStr*', # declared in containers.h
55 'int': 'int',
56 'uint16': 'uint16_t',
57 'BigInt': 'mops::BigInt',
58 'float': 'double',
59 'bool': 'bool',
60 'any': 'void*',
61 # TODO: frontend/syntax.asdl should properly import id enum instead of
62 # hard-coding it here.
63 'id': 'Id_t',
64}
65
66
67class ForwardDeclareVisitor(visitor.AsdlVisitor):
68 """Print forward declarations.
69
70 ASDL allows forward references of types, but C++ doesn't.
71 """
72
73 def VisitCompoundSum(self, sum, name, depth):
74 self.Emit("class %(name)s_t;" % locals(), depth)
75
76 def VisitProduct(self, product, name, depth):
77 self.Emit("class %(name)s;" % locals(), depth)
78
79 def EmitFooter(self):
80 self.Emit("", 0) # blank line
81
82
83def _GetCppType(typ):
84 if isinstance(typ, ast.ParameterizedType):
85 type_name = typ.type_name
86
87 if type_name == 'Dict':
88 k_type = _GetCppType(typ.children[0])
89 v_type = _GetCppType(typ.children[1])
90 return 'Dict<%s, %s>*' % (k_type, v_type)
91
92 elif type_name == 'List':
93 c_type = _GetCppType(typ.children[0])
94 return 'List<%s>*' % (c_type)
95
96 elif type_name == 'Optional':
97 c_type = _GetCppType(typ.children[0])
98 return c_type
99
100 elif isinstance(typ, ast.NamedType):
101
102 if typ.resolved:
103 if isinstance(typ.resolved, ast.SimpleSum):
104 return '%s_t' % typ.name
105 if isinstance(typ.resolved, ast.Sum):
106 return '%s_t*' % typ.name
107 if isinstance(typ.resolved, ast.Product):
108 return '%s*' % typ.name
109 if isinstance(typ.resolved, ast.Use):
110 return '%s_asdl::%s*' % (typ.resolved.module_parts[-1],
111 ast.TypeNameHeuristic(typ.name))
112
113 # 'id' falls through here
114 return _PRIMITIVES[typ.name]
115
116 else:
117 raise AssertionError()
118
119
120def _IsManagedType(typ):
121 # This is a little cheesy, but works
122 return _GetCppType(typ).endswith('*')
123
124
125def _DefaultValue(typ, conditional=True):
126 """Values that the ::CreateNull() constructor passes."""
127
128 if isinstance(typ, ast.ParameterizedType):
129 type_name = typ.type_name
130
131 if type_name == 'Dict': # TODO: can respect alloc_dicts=True
132 return 'nullptr'
133
134 elif type_name == 'List':
135 c_type = _GetCppType(typ.children[0])
136
137 d = 'Alloc<List<%s>>()' % (c_type)
138 if conditional:
139 return 'alloc_lists ? %s : nullptr' % d
140 else:
141 return d
142
143 elif type_name == 'Optional':
144 return 'nullptr'
145
146 else:
147 raise AssertionError(type_name)
148
149 elif isinstance(typ, ast.NamedType):
150 type_name = typ.name
151
152 if type_name in ('int', 'uint16', 'BigInt'):
153 default = '-1'
154 elif type_name == 'id': # hard-coded HACK
155 default = '-1'
156 elif type_name == 'bool':
157 default = 'false'
158 elif type_name == 'float':
159 default = '0.0' # or should it be NaN?
160 elif type_name == 'string':
161 default = 'kEmptyString'
162
163 elif typ.resolved and isinstance(typ.resolved, ast.SimpleSum):
164 sum_type = typ.resolved
165 # Just make it the first variant. We could define "Undef" for
166 # each enum, but it doesn't seem worth it.
167 default = '%s_e::%s' % (type_name, sum_type.types[0].name)
168
169 else:
170 default = 'nullptr' # Sum or Product
171 return default
172
173 else:
174 raise AssertionError()
175
176
177def _HNodeExpr(abbrev, typ, var_name):
178 # type: (str, ast.TypeExpr, str) -> str
179 none_guard = False
180
181 if typ.IsOptional():
182 typ = typ.children[0] # descend one level
183
184 if isinstance(typ, ast.ParameterizedType):
185 code_str = '%s->%s()' % (var_name, abbrev)
186 none_guard = True
187
188 elif isinstance(typ, ast.NamedType):
189
190 type_name = typ.name
191
192 if type_name == 'bool':
193 code_str = "Alloc<hnode::Leaf>(%s ? runtime::TRUE_STR : runtime::FALSE_STR, color_e::OtherConst)" % var_name
194
195 elif type_name in ('int', 'uint16'):
196 code_str = 'Alloc<hnode::Leaf>(str(%s), color_e::OtherConst)' % var_name
197
198 elif type_name == 'BigInt':
199 code_str = 'Alloc<hnode::Leaf>(mops::ToStr(%s), color_e::OtherConst)' % var_name
200
201 elif type_name == 'float':
202 code_str = 'Alloc<hnode::Leaf>(str(%s), color_e::OtherConst)' % var_name
203
204 elif type_name == 'string':
205 code_str = 'runtime::NewLeaf(%s, color_e::StringConst)' % var_name
206
207 elif type_name == 'any': # TODO: Remove this. Used for value.Obj().
208 code_str = 'Alloc<hnode::External>(%s)' % var_name
209
210 elif type_name == 'id': # was meta.UserType
211 code_str = 'Alloc<hnode::Leaf>(Id_str(%s), color_e::UserType)' % var_name
212
213 elif typ.resolved and isinstance(typ.resolved, ast.SimpleSum):
214 code_str = 'Alloc<hnode::Leaf>(%s_str(%s), color_e::TypeName)' % (
215 type_name, var_name)
216
217 else:
218 code_str = '%s->%s(seen)' % (var_name, abbrev)
219 none_guard = True
220
221 else:
222 raise AssertionError()
223
224 return code_str, none_guard
225
226
227class ClassDefVisitor(visitor.AsdlVisitor):
228 """Generate C++ declarations and type-safe enums."""
229
230 def __init__(self, f, pretty_print_methods=True, debug_info=None):
231 """
232 Args:
233 f: file to write to
234 debug_info: dictionary fill in with info for GDB
235 """
236 visitor.AsdlVisitor.__init__(self, f)
237 self.pretty_print_methods = pretty_print_methods
238 self.debug_info = debug_info if debug_info is not None else {}
239
240 self._shared_type_tags = {}
241 self._product_counter = 64 # start halfway through the range 0-127
242
243 self._products = []
244 self._base_classes = defaultdict(list)
245
246 self._subtypes = []
247
248 def _EmitEnum(self, sum, sum_name, depth, strong=False, is_simple=False):
249 enum = []
250 int_to_type = {}
251 add_suffix = not ('no_namespace_suffix' in sum.generate)
252 for i, variant in enumerate(sum.types):
253 if variant.shared_type: # Copied from gen_python.py
254 tag_num = self._shared_type_tags[variant.shared_type]
255 # e.g. DoubleQuoted may have base types expr_t, word_part_t
256 base_class = sum_name + '_t'
257 bases = self._base_classes[variant.shared_type]
258 if base_class in bases:
259 raise RuntimeError(
260 "Two tags in sum %r refer to product type %r" %
261 (sum_name, variant.shared_type))
262 else:
263 bases.append(base_class)
264 type_str = variant.shared_type
265 else:
266 tag_num = i + 1
267 type_str = '%s__%s' % (sum_name, variant.name)
268 int_to_type[tag_num] = type_str
269 enum.append((variant.name, tag_num)) # zero is reserved
270
271 if strong:
272 enum_name = '%s_e' % sum_name if add_suffix else sum_name
273
274 # Simple sum types can be STRONG since there's no possibility of multiple
275 # inheritance!
276
277 self.Emit('enum class %s {' % enum_name, depth)
278 for name, tag_num in enum:
279 self.Emit('%s = %d,' % (name, tag_num), depth + 1)
280 self.Emit('};', depth)
281
282 # type alias to match Python code
283 self.Emit('typedef %s %s_t;' % (enum_name, sum_name), depth)
284 self.Emit('', depth)
285
286 if self.pretty_print_methods:
287 self.Emit(
288 'BigStr* %s_str(%s tag, bool dot = true);' %
289 (sum_name, enum_name), depth)
290 self.Emit('', depth)
291
292 else:
293 if is_simple:
294 enum_name = '%s_i' % sum_name if add_suffix else sum_name
295 else:
296 enum_name = '%s_e' % sum_name if add_suffix else sum_name
297
298 # Awkward struct/enum C++ idiom because:
299
300 # 1. namespace can't be "imported" with 'using'
301 # 2. plain enum pollutes outer namespace
302 # 3. C++ 11 'enum class' does not allow conversion to int
303 # 4. namespace and 'static const int' or 'static constexpr int' gives
304 # weird link errors
305 # https://quuxplusone.github.io/blog/2020/09/19/value-or-pitfall/
306
307 self.Emit('ASDL_NAMES %s {' % enum_name, depth)
308 self.Emit(' enum no_name {', depth)
309 for name, tag_num in enum:
310 self.Emit('%s = %d,' % (name, tag_num), depth + 1)
311
312 if is_simple:
313 # Help in sizing array. Note that we're 1-based.
314 self.Emit('ARRAY_SIZE = %d,' % (len(enum) + 1), depth + 1)
315
316 self.Emit(' };', depth)
317 self.Emit('};', depth)
318
319 self.Emit('', depth)
320
321 if self.pretty_print_methods:
322 self.Emit(
323 'BigStr* %s_str(int tag, bool dot = true);' % sum_name,
324 depth)
325 self.Emit('', depth)
326
327 return int_to_type
328
329 def VisitSimpleSum(self, sum, name, depth):
330 # Note: there can be more than 128 variants in a simple sum, because it's an
331 # integer and doesn't have an object header.
332
333 if 'integers' in sum.generate:
334 self._EmitEnum(sum, name, depth, strong=False, is_simple=True)
335 self.Emit('typedef int %s_t;' % name)
336 self.Emit('')
337 elif 'uint16' in sum.generate:
338 self._EmitEnum(sum, name, depth, strong=False, is_simple=True)
339 self.Emit('typedef uint16_t %s_t;' % name)
340 self.Emit('')
341 else:
342 self._EmitEnum(sum, name, depth, strong=True)
343
344 def VisitCompoundSum(self, sum, sum_name, depth):
345 #log('%d variants in %s', len(sum.types), sum_name)
346
347 # Must fit in 7 bit Obj::type_tag
348 assert len(
349 sum.types) < 64, 'sum type %r has too many variants' % sum_name
350
351 # This is a sign that Python needs string interpolation!!!
352 def Emit(s, depth=depth):
353 self.Emit(s % sys._getframe(1).f_locals, depth)
354
355 int_to_type = self._EmitEnum(sum, sum_name, depth)
356
357 # Only add debug info for compound sums.
358 self.debug_info['%s_t' % sum_name] = int_to_type
359
360 # This is the base class.
361 Emit('class %(sum_name)s_t {')
362 # Can't be constructed directly. Note: this shows up in uftrace in debug
363 # mode, e.g. when we instantiate Token. Do we need it?
364 Emit(' protected:')
365 Emit(' %s_t() {' % sum_name)
366 Emit(' }')
367 Emit(' public:')
368 Emit(' int tag() const {')
369 # There's no inheritance relationship, so we have to reinterpret_cast.
370 Emit(' return ObjHeader::FromObject(this)->type_tag;')
371 Emit(' }')
372
373 if self.pretty_print_methods:
374 for abbrev in PRETTY_METHODS:
375 self.Emit(' hnode_t* %s(Dict<int, bool>* seen = nullptr);' %
376 abbrev)
377
378 Emit(' DISALLOW_COPY_AND_ASSIGN(%(sum_name)s_t)')
379 Emit('};')
380 Emit('')
381
382 for variant in sum.types:
383 if variant.shared_type:
384 # Don't generate a class.
385 pass
386 else:
387 super_name = '%s_t' % sum_name
388 tag = 'static_cast<uint16_t>(%s_e::%s)' % (sum_name,
389 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 _GenClass(self, fields, class_name, base_classes, depth, tag):
423 """For Product and Constructor."""
424 if base_classes:
425 bases = ', '.join('public %s' % b for b in base_classes)
426 self.Emit("class %s : %s {" % (class_name, bases), depth)
427 else:
428 self.Emit("class %s {" % class_name, depth)
429 self.Emit(" public:", depth)
430
431 # Ensure that the member variables are ordered such that GC managed objects
432 # come before any unmanaged ones because we use `HeapTag::Scanned`.
433 managed_fields, unmanaged_fields = [], []
434 for f in fields:
435 if _IsManagedType(f.typ):
436 managed_fields.append(f)
437 else:
438 unmanaged_fields.append(f)
439 all_fields = managed_fields + unmanaged_fields
440
441 def FieldInitJoin(strs):
442 # reflow doesn't work well here, so do it manually
443 return ',\n '.join(strs)
444
445 # Ensure that the constructor params are listed in the same order as the
446 # equivalent python constructors for compatibility in translated code.
447 params = []
448 for f in fields:
449 params.append('%s %s' % (_GetCppType(f.typ), f.name))
450
451 # Member initializers are in the same order as the member variables to
452 # avoid compiler warnings (the order doesn't affect the semantics).
453 inits = []
454 for f in all_fields:
455 inits.append('%s(%s)' % (f.name, f.name))
456
457 # Define constructor with N args
458 if len(inits):
459 self.Emit(' %s(%s)' % (class_name, ', '.join(params)), depth)
460 self.Emit(' : %s {' % FieldInitJoin(inits),
461 depth,
462 reflow=False)
463 self.Emit(' }')
464 else:
465 self.Emit(' %s(%s) {}' % (class_name, ', '.join(params)), depth)
466 self.Emit('')
467
468 # Define static constructor with ZERO args. Don't emit for types with no
469 # fields.
470 if fields:
471 init_args = []
472 for field in fields:
473 init_args.append(_DefaultValue(field.typ))
474
475 self.Emit(
476 ' static %s* CreateNull(bool alloc_lists = false) { ' %
477 class_name, depth)
478 self.Emit(
479 ' return Alloc<%s>(%s);' %
480 (class_name, ', '.join(init_args)), depth)
481 self.Emit(' }')
482 self.Emit('')
483
484 if self.pretty_print_methods:
485 for abbrev in PRETTY_METHODS:
486 self.Emit(
487 ' hnode_t* %s(Dict<int, bool>* seen = nullptr);' % abbrev,
488 depth)
489 self.Emit('')
490
491 self.Emit(' static constexpr ObjHeader obj_header() {')
492 self.Emit(' return ObjHeader::AsdlClass(%s, %d);' %
493 (tag, len(managed_fields)))
494 self.Emit(' }')
495 self.Emit('')
496
497 #
498 # Members
499 #
500 for field in all_fields:
501 self.Emit(" %s %s;" % (_GetCppType(field.typ), field.name))
502
503 self.Emit('')
504 self.Emit(' DISALLOW_COPY_AND_ASSIGN(%s)' % class_name)
505 self.Emit('};', depth)
506 self.Emit('', depth)
507
508 def VisitSubType(self, subtype):
509 self._shared_type_tags[subtype.name] = self._product_counter
510
511 # Also create these last. They may inherit from sum types that have yet
512 # to be defined.
513 self._subtypes.append((subtype, self._product_counter))
514 self._product_counter += 1
515
516 def VisitProduct(self, product, name, depth):
517 self._shared_type_tags[name] = self._product_counter
518 # Create a tuple of _GenClass args to create LAST. They may inherit from
519 # sum types that have yet to be defined.
520 self._products.append((product, name, depth, self._product_counter))
521 self._product_counter += 1
522
523 def EmitFooter(self):
524 # Now generate all the product types we deferred.
525 for args in self._products:
526 ast_node, name, depth, tag_num = args
527 # Figure out base classes AFTERWARD.
528 bases = self._base_classes[name]
529 self._GenClass(ast_node.fields, name, bases, depth, tag_num)
530
531 for args in self._subtypes:
532 subtype, tag_num = args
533 # Figure out base classes AFTERWARD.
534 bases = self._base_classes[subtype.name]
535
536 cpp_type = _GetCppType(subtype.base_class)
537 assert cpp_type.endswith('*') # hack
538 bases.append(cpp_type[:-1])
539
540 # TODO:
541 # - Change ObjHeader
542 # - Change pretty printing to be like a List
543 self._GenClass([], subtype.name, bases, 0, tag_num)
544
545
546class MethodDefVisitor(visitor.AsdlVisitor):
547 """Generate the body of pretty printing methods.
548
549 We have to do this in another pass because types and schemas have
550 circular dependencies.
551 """
552
553 def __init__(self, f, pretty_print_methods=True):
554 visitor.AsdlVisitor.__init__(self, f)
555
556 def _EmitCodeForField(self, abbrev, field, counter):
557 """Generate code that returns an hnode for a field."""
558 out_val_name = 'x%d' % counter
559
560 if field.typ.IsList():
561 iter_name = 'i%d' % counter
562
563 typ = field.typ
564 if typ.type_name == 'Optional': # descend one level
565 typ = typ.children[0]
566 item_type = typ.children[0]
567
568 self.Emit('if (this->%s != nullptr) { // List' % (field.name))
569 self.Emit(
570 ' hnode::Array* %s = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());'
571 % out_val_name)
572 c_item_type = _GetCppType(item_type)
573 self.Emit(
574 ' for (ListIter<%s> it(this->%s); !it.Done(); it.Next()) {' %
575 (c_item_type, field.name))
576 self.Emit(' %s %s = it.Value();' % (c_item_type, iter_name))
577
578 child_code_str, none_guard = _HNodeExpr(abbrev, item_type,
579 iter_name)
580 if none_guard: # e.g. for List[Optional[value_t]]
581 # TODO: could consolidate with asdl/runtime.py NewLeaf(), which
582 # also uses _ to mean None/nullptr
583 self.Emit(
584 ' hnode_t* h = (%s == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"), color_e::OtherConst) : %s;'
585 % (iter_name, child_code_str))
586 self.Emit(' %s->children->append(h);' % out_val_name)
587 else:
588 self.Emit(' %s->children->append(%s);' %
589 (out_val_name, child_code_str))
590
591 self.Emit(' }')
592 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
593 (field.name, out_val_name))
594 self.Emit('}')
595
596 elif field.typ.IsDict():
597 k = 'k%d' % counter
598 v = 'v%d' % counter
599
600 typ = field.typ
601 if typ.type_name == 'Optional': # descend one level
602 typ = typ.children[0]
603
604 k_typ = typ.children[0]
605 v_typ = typ.children[1]
606
607 k_c_type = _GetCppType(k_typ)
608 v_c_type = _GetCppType(v_typ)
609
610 k_code_str, _ = _HNodeExpr(abbrev, k_typ, k)
611 v_code_str, _ = _HNodeExpr(abbrev, v_typ, v)
612
613 self.Emit('if (this->%s) { // Dict' % field.name)
614 # TODO: m can be a global constant!
615 self.Emit(
616 ' auto m = Alloc<hnode::Leaf>(StrFromC("Dict"), color_e::OtherConst);'
617 )
618 self.Emit(
619 ' hnode::Array* %s = Alloc<hnode::Array>(NewList<hnode_t*>({m}));'
620 % out_val_name)
621 self.Emit(
622 ' for (DictIter<%s, %s> it(this->%s); !it.Done(); it.Next()) {'
623 % (k_c_type, v_c_type, field.name))
624 self.Emit(' auto %s = it.Key();' % k)
625 self.Emit(' auto %s = it.Value();' % v)
626 self.Emit(' %s->children->append(%s);' %
627 (out_val_name, k_code_str))
628 self.Emit(' %s->children->append(%s);' %
629 (out_val_name, v_code_str))
630 self.Emit(' }')
631 self.Emit(' L->append(Alloc<Field>(StrFromC ("%s"), %s));' %
632 (field.name, out_val_name))
633 self.Emit('}')
634
635 elif field.typ.IsOptional():
636 typ = field.typ.children[0]
637
638 self.Emit('if (this->%s) { // Optional' % field.name)
639 child_code_str, _ = _HNodeExpr(abbrev, typ,
640 'this->%s' % field.name)
641 self.Emit(' hnode_t* %s = %s;' % (out_val_name, child_code_str))
642 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
643 (field.name, out_val_name))
644 self.Emit('}')
645
646 else:
647 var_name = 'this->%s' % field.name
648 code_str, obj_none_guard = _HNodeExpr(abbrev, field.typ, var_name)
649
650 depth = self.current_depth
651 if obj_none_guard: # to satisfy MyPy type system
652 pass
653 self.Emit('hnode_t* %s = %s;' % (out_val_name, code_str), depth)
654
655 self.Emit(
656 'L->append(Alloc<Field>(StrFromC("%s"), %s));' %
657 (field.name, out_val_name), depth)
658
659 def _EmitPrettyPrintMethods(self, class_name, all_fields, sum_name=None):
660 #
661 # PrettyTree
662 #
663
664 if sum_name is not None:
665 n = '%s_str(this->tag())' % sum_name
666 else:
667 n = 'StrFromC("%s")' % class_name
668
669 self.Emit('')
670 self.Emit('hnode_t* %s::PrettyTree(Dict<int, bool>* seen) {' %
671 class_name)
672
673 # Similar to j8::HeapValueId()
674 self.Emit(' seen = seen ? seen : Alloc<Dict<int, bool>>();')
675 self.Emit(' int heap_id = ObjectId(this);')
676 self.Emit(' if (dict_contains(seen, heap_id)) {')
677 self.Emit(' return Alloc<hnode::AlreadySeen>(heap_id);')
678 self.Emit(' }')
679 self.Emit(' seen->set(heap_id, true);')
680
681 self.Emit(' hnode::Record* out_node = runtime::NewRecord(%s);' % n)
682 if all_fields:
683 self.Emit(' List<Field*>* L = out_node->fields;')
684 self.Emit('')
685
686 # Use the runtime type to be more like asdl/format.py
687 for local_id, field in enumerate(all_fields):
688 #log('%s :: %s', field_name, field_desc)
689 self.Indent()
690 self._EmitCodeForField('PrettyTree', field, local_id)
691 self.Dedent()
692 self.Emit('')
693 self.Emit(' return out_node;')
694 self.Emit('}')
695 self.Emit('')
696
697 #
698 # _AbbreviatedTree
699 #
700
701 if not ABBREV:
702 return
703
704 self.Emit('')
705 self.Emit('hnode_t* %s::_AbbreviatedTree() {' % class_name)
706 self.Emit(' hnode::Record* out_node = runtime::NewRecord("%s");' % n)
707 if all_fields:
708 self.Emit(' List<Field*>* L = out_node->fields;')
709
710 for local_id, field in enumerate(all_fields):
711 self.Indent()
712 self._EmitCodeForField('AbbreviatedTree', field, local_id)
713 self.Dedent()
714 self.Emit('')
715 self.Emit(' return out_node;')
716 self.Emit('}')
717 self.Emit('')
718
719 self.Emit('hnode_t* %s::AbbreviatedTree() {' % class_name)
720 abbrev_name = '_%s' % class_name
721
722 # STUB
723 self.abbrev_mod_entries = []
724
725 if abbrev_name in self.abbrev_mod_entries:
726 self.Emit(' hnode_t* p = %s();' % abbrev_name)
727 # If the user function didn't return anything, fall back.
728 self.Emit(' return p ? p : _AbbreviatedTree();')
729 else:
730 self.Emit(' return _AbbreviatedTree();')
731 self.Emit('}')
732
733 def _EmitStrFunction(self,
734 sum,
735 sum_name,
736 depth,
737 strong=False,
738 simple=False):
739 add_suffix = not ('no_namespace_suffix' in sum.generate)
740 if add_suffix:
741 if simple:
742 enum_name = '%s_i' % sum_name
743 else:
744 enum_name = '%s_e' % sum_name
745 else:
746 enum_name = sum_name
747
748 if strong:
749 self.Emit(
750 'BigStr* %s_str(%s tag, bool dot) {' % (sum_name, enum_name),
751 depth)
752 else:
753 self.Emit('BigStr* %s_str(int tag, bool dot) {' % sum_name, depth)
754
755 buf_size = 32
756 v_max = max(len(variant.name) for variant in sum.types)
757 s_max = v_max + 1 + len(sum_name) + 1 # for . and NUL
758 if s_max > buf_size:
759 raise RuntimeError('Sum name %r + variant name is too long' %
760 sum_name)
761
762 self.Emit(' char buf[%d];' % buf_size, depth)
763 self.Emit(' const char* v = nullptr;', depth)
764 self.Emit(' switch (tag) {', depth)
765 for variant in sum.types:
766 self.Emit('case %s::%s:' % (enum_name, variant.name), depth + 1)
767 self.Emit(' v = "%s"; break;' % variant.name, depth + 1)
768
769 self.Emit('default:', depth + 1)
770 self.Emit(' assert(0);', depth + 1)
771
772 self.Emit(' }', depth)
773 self.Emit(' if (dot) {', depth)
774 self.Emit(' snprintf(buf, %d, "%s.%%s", v);' % (buf_size, sum_name),
775 depth)
776 self.Emit(' return StrFromC(buf);', depth)
777 self.Emit(' } else {', depth)
778 self.Emit(' return StrFromC(v);', depth)
779 self.Emit(' }', depth)
780 self.Emit('}', depth)
781
782 def VisitSimpleSum(self, sum, name, depth):
783 if 'integers' in sum.generate or 'uint16' in sum.generate:
784 self._EmitStrFunction(sum, name, depth, strong=False, simple=True)
785 else:
786 self._EmitStrFunction(sum, name, depth, strong=True)
787
788 def VisitCompoundSum(self, sum, sum_name, depth):
789 self._EmitStrFunction(sum, sum_name, depth)
790
791 # Generate definitions for the for zero arg singleton globals
792 for variant in sum.types:
793 if variant.shared_type:
794 continue
795 if len(variant.fields) == 0:
796 variant_name = variant.name
797 self.Emit('')
798 self.Emit('%s__%s* %s::%s = &g%s__%s.obj;' %
799 (sum_name, variant_name, sum_name, variant_name,
800 sum_name, variant_name))
801 self.Emit('')
802 self.Emit('GcGlobal<%s__%s> g%s__%s = ' %
803 (sum_name, variant_name, sum_name, variant_name))
804 self.Emit(' { ObjHeader::Global(%s_e::%s) };' %
805 (sum_name, variant_name))
806
807 for variant in sum.types:
808 if variant.shared_type:
809 continue
810 all_fields = variant.fields
811 class_name = '%s__%s' % (sum_name, variant.name)
812 self._EmitPrettyPrintMethods(class_name,
813 all_fields,
814 sum_name=sum_name)
815
816 # Emit dispatch WITHOUT using 'virtual'
817 for func_name in PRETTY_METHODS:
818 self.Emit('')
819 self.Emit('hnode_t* %s_t::%s(Dict<int, bool>* seen) {' %
820 (sum_name, func_name))
821 self.Emit(' switch (this->tag()) {', depth)
822
823 for variant in sum.types:
824 if variant.shared_type:
825 subtype_name = variant.shared_type
826 else:
827 subtype_name = '%s__%s' % (sum_name, variant.name)
828
829 self.Emit(' case %s_e::%s: {' % (sum_name, variant.name),
830 depth)
831 self.Emit(
832 ' %s* obj = static_cast<%s*>(this);' %
833 (subtype_name, subtype_name), depth)
834 self.Emit(' return obj->%s(seen);' % func_name, depth)
835 self.Emit(' }', depth)
836
837 self.Emit(' default:', depth)
838 self.Emit(' assert(0);', depth)
839
840 self.Emit(' }')
841 self.Emit('}')
842
843 def VisitProduct(self, product, name, depth):
844 self._EmitPrettyPrintMethods(name, product.fields)
845
846 def VisitSubType(self, subtype):
847 pass
848 #self._EmitPrettyPrintMethods(subtype.name, [], product)