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

851 lines, 567 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,
660 class_name,
661 all_fields,
662 ast_node,
663 sum_name=None):
664 #
665 # PrettyTree
666 #
667
668 if sum_name is not None:
669 n = '%s_str(this->tag())' % sum_name
670 else:
671 n = 'StrFromC("%s")' % class_name
672
673 self.Emit('')
674 self.Emit('hnode_t* %s::PrettyTree(Dict<int, bool>* seen) {' %
675 class_name)
676
677 # Similar to j8::HeapValueId()
678 self.Emit(' seen = seen ? seen : Alloc<Dict<int, bool>>();')
679 self.Emit(' int heap_id = ObjectId(this);')
680 self.Emit(' if (dict_contains(seen, heap_id)) {')
681 self.Emit(' return Alloc<hnode::AlreadySeen>(heap_id);')
682 self.Emit(' }')
683 self.Emit(' seen->set(heap_id, true);')
684
685 self.Emit(' hnode::Record* out_node = runtime::NewRecord(%s);' % n)
686 if all_fields:
687 self.Emit(' List<Field*>* L = out_node->fields;')
688 self.Emit('')
689
690 # Use the runtime type to be more like asdl/format.py
691 for local_id, field in enumerate(all_fields):
692 #log('%s :: %s', field_name, field_desc)
693 self.Indent()
694 self._EmitCodeForField('PrettyTree', field, local_id)
695 self.Dedent()
696 self.Emit('')
697 self.Emit(' return out_node;')
698 self.Emit('}')
699 self.Emit('')
700
701 #
702 # _AbbreviatedTree
703 #
704
705 if not ABBREV:
706 return
707
708 self.Emit('')
709 self.Emit('hnode_t* %s::_AbbreviatedTree() {' % class_name)
710 self.Emit(' hnode::Record* out_node = runtime::NewRecord("%s");' % n)
711 if ast_node.fields:
712 self.Emit(' List<Field*>* L = out_node->fields;')
713
714 for local_id, field in enumerate(ast_node.fields):
715 self.Indent()
716 self._EmitCodeForField('AbbreviatedTree', field, local_id)
717 self.Dedent()
718 self.Emit('')
719 self.Emit(' return out_node;')
720 self.Emit('}')
721 self.Emit('')
722
723 self.Emit('hnode_t* %s::AbbreviatedTree() {' % class_name)
724 abbrev_name = '_%s' % class_name
725
726 # STUB
727 self.abbrev_mod_entries = []
728
729 if abbrev_name in self.abbrev_mod_entries:
730 self.Emit(' hnode_t* p = %s();' % abbrev_name)
731 # If the user function didn't return anything, fall back.
732 self.Emit(' return p ? p : _AbbreviatedTree();')
733 else:
734 self.Emit(' return _AbbreviatedTree();')
735 self.Emit('}')
736
737 def _EmitStrFunction(self,
738 sum,
739 sum_name,
740 depth,
741 strong=False,
742 simple=False):
743 add_suffix = not ('no_namespace_suffix' in sum.generate)
744 if add_suffix:
745 if simple:
746 enum_name = '%s_i' % sum_name
747 else:
748 enum_name = '%s_e' % sum_name
749 else:
750 enum_name = sum_name
751
752 if strong:
753 self.Emit(
754 'BigStr* %s_str(%s tag, bool dot) {' % (sum_name, enum_name),
755 depth)
756 else:
757 self.Emit('BigStr* %s_str(int tag, bool dot) {' % sum_name, depth)
758
759 buf_size = 32
760 v_max = max(len(variant.name) for variant in sum.types)
761 s_max = v_max + 1 + len(sum_name) + 1 # for . and NUL
762 if s_max > buf_size:
763 raise RuntimeError('Sum name %r + variant name is too long' %
764 sum_name)
765
766 self.Emit(' char buf[%d];' % buf_size, depth)
767 self.Emit(' const char* v = nullptr;', depth)
768 self.Emit(' switch (tag) {', depth)
769 for variant in sum.types:
770 self.Emit('case %s::%s:' % (enum_name, variant.name), depth + 1)
771 self.Emit(' v = "%s"; break;' % variant.name, depth + 1)
772
773 self.Emit('default:', depth + 1)
774 self.Emit(' assert(0);', depth + 1)
775
776 self.Emit(' }', depth)
777 self.Emit(' if (dot) {', depth)
778 self.Emit(' snprintf(buf, %d, "%s.%%s", v);' % (buf_size, sum_name),
779 depth)
780 self.Emit(' return StrFromC(buf);', depth)
781 self.Emit(' } else {', depth)
782 self.Emit(' return StrFromC(v);', depth)
783 self.Emit(' }', depth)
784 self.Emit('}', depth)
785
786 def VisitSimpleSum(self, sum, name, depth):
787 if 'integers' in sum.generate or 'uint16' in sum.generate:
788 self._EmitStrFunction(sum, name, depth, strong=False, simple=True)
789 else:
790 self._EmitStrFunction(sum, name, depth, strong=True)
791
792 def VisitCompoundSum(self, sum, sum_name, depth):
793 self._EmitStrFunction(sum, sum_name, depth)
794
795 # Generate definitions for the for zero arg singleton globals
796 for variant in sum.types:
797 if variant.shared_type:
798 continue
799 if len(variant.fields) == 0:
800 variant_name = variant.name
801 self.Emit('')
802 self.Emit('%s__%s* %s::%s = &g%s__%s.obj;' %
803 (sum_name, variant_name, sum_name, variant_name,
804 sum_name, variant_name))
805 self.Emit('')
806 self.Emit('GcGlobal<%s__%s> g%s__%s = ' %
807 (sum_name, variant_name, sum_name, variant_name))
808 self.Emit(' { ObjHeader::Global(%s_e::%s) };' %
809 (sum_name, variant_name))
810
811 for variant in sum.types:
812 if variant.shared_type:
813 continue
814 all_fields = variant.fields
815 class_name = '%s__%s' % (sum_name, variant.name)
816 self._EmitPrettyPrintMethods(class_name,
817 all_fields,
818 variant,
819 sum_name=sum_name)
820
821 # Emit dispatch WITHOUT using 'virtual'
822 for func_name in PRETTY_METHODS:
823 self.Emit('')
824 self.Emit('hnode_t* %s_t::%s(Dict<int, bool>* seen) {' %
825 (sum_name, func_name))
826 self.Emit(' switch (this->tag()) {', depth)
827
828 for variant in sum.types:
829 if variant.shared_type:
830 subtype_name = variant.shared_type
831 else:
832 subtype_name = '%s__%s' % (sum_name, variant.name)
833
834 self.Emit(' case %s_e::%s: {' % (sum_name, variant.name),
835 depth)
836 self.Emit(
837 ' %s* obj = static_cast<%s*>(this);' %
838 (subtype_name, subtype_name), depth)
839 self.Emit(' return obj->%s(seen);' % func_name, depth)
840 self.Emit(' }', depth)
841
842 self.Emit(' default:', depth)
843 self.Emit(' assert(0);', depth)
844
845 self.Emit(' }')
846 self.Emit('}')
847
848 def VisitProduct(self, product, name, depth):
849 #self._GenClass(product, product.attributes, name, None, depth)
850 all_fields = product.fields
851 self._EmitPrettyPrintMethods(name, all_fields, product)