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

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