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

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