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

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