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

917 lines, 617 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 List, Dict, Tuple, IO, Any, 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__(
227 self,
228 f, # type: IO
229 pretty_print_methods=True, # type: bool
230 debug_info=None, # Optional[Dict[str, int]]
231 ):
232 # type: (...) -> None
233 """
234 Args:
235 f: file to write to
236 debug_info: dictionary fill in with info for GDB
237 """
238 visitor.AsdlVisitor.__init__(self, f)
239 self.pretty_print_methods = pretty_print_methods
240 self.debug_info = debug_info if debug_info is not None else {}
241
242 self._shared_type_tags = {} # type: Dict[str, int]
243 self._product_counter = MAX_VARIANTS_PER_SUM
244
245 self._products = [] # type: List[Any]
246 self._base_classes = defaultdict(list) # type: Dict[str, List[str]]
247
248 self._subtypes = [] # type: List[Any]
249
250 def _EmitEnum(self, sum, sum_name, depth, strong=False, is_simple=False):
251 enum = []
252 int_to_type = {}
253 add_suffix = not ('no_namespace_suffix' in sum.generate)
254 for i, variant in enumerate(sum.types):
255 if variant.shared_type: # Copied from gen_python.py
256 tag_num = self._shared_type_tags[variant.shared_type]
257 # e.g. DoubleQuoted may have base types expr_t, word_part_t
258 base_class = sum_name + '_t'
259 bases = self._base_classes[variant.shared_type]
260 if base_class in bases:
261 raise RuntimeError(
262 "Two tags in sum %r refer to product type %r" %
263 (sum_name, variant.shared_type))
264 else:
265 bases.append(base_class)
266 type_str = variant.shared_type
267 else:
268 tag_num = i + 1
269 type_str = '%s__%s' % (sum_name, variant.name)
270 int_to_type[tag_num] = type_str
271 enum.append((variant.name, tag_num)) # zero is reserved
272
273 if strong:
274 enum_name = '%s_e' % sum_name if add_suffix else sum_name
275
276 # Simple sum types can be STRONG since there's no possibility of multiple
277 # inheritance!
278
279 self.Emit('enum class %s {' % enum_name, depth)
280 for name, tag_num in enum:
281 self.Emit('%s = %d,' % (name, tag_num), depth + 1)
282 self.Emit('};', depth)
283
284 # type alias to match Python code
285 self.Emit('typedef %s %s_t;' % (enum_name, sum_name), depth)
286 self.Emit('', depth)
287
288 if self.pretty_print_methods:
289 sum_name = ast.NameHack(sum_name)
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 sum_name = ast.NameHack(sum_name)
326 self.Emit(
327 'BigStr* %s_str(int tag, bool dot = true);' % sum_name,
328 depth)
329 self.Emit('', depth)
330
331 return int_to_type
332
333 def VisitSimpleSum(self, sum, name, depth):
334 # Note: there can be more than 128 variants in a simple sum, because it's an
335 # integer and doesn't have an object header.
336
337 if 'integers' in sum.generate:
338 self._EmitEnum(sum, name, depth, strong=False, is_simple=True)
339 self.Emit('typedef int %s_t;' % name)
340 self.Emit('')
341 elif 'uint16' in sum.generate:
342 self._EmitEnum(sum, name, depth, strong=False, is_simple=True)
343 self.Emit('typedef uint16_t %s_t;' % name)
344 self.Emit('')
345 else:
346 self._EmitEnum(sum, name, depth, strong=True)
347
348 def VisitCompoundSum(self, sum, sum_name, depth):
349 #log('%d variants in %s', len(sum.types), sum_name)
350
351 # Must fit in 7 bit Obj::type_tag
352 assert len(sum.types) < MAX_VARIANTS_PER_SUM, \
353 'sum type %r has too many variants' % sum_name
354
355 # This is a sign that Python needs string interpolation!!!
356 def Emit(s, depth=depth):
357 self.Emit(s % sys._getframe(1).f_locals, depth)
358
359 int_to_type = self._EmitEnum(sum, sum_name, depth)
360
361 # Only add debug info for compound sums.
362 self.debug_info['%s_t' % sum_name] = int_to_type
363
364 # This is the base class.
365 Emit('class %(sum_name)s_t {')
366
367 # to implement Walkable, we'd need all types to fit in in the GC header
368 #Emit('class %(sum_name)s_t : public Walkable {')
369
370 # Can't be constructed directly. Note: this shows up in uftrace in debug
371 # mode, e.g. when we instantiate Token. Do we need it?
372 Emit(' protected:')
373 Emit(' %s_t() {' % sum_name)
374 Emit(' }')
375 Emit(' public:')
376 Emit(' int tag() const {')
377 # There's no inheritance relationship, so we have to reinterpret_cast.
378 Emit(' return ObjHeader::FromObject(this)->type_tag;')
379 Emit(' }')
380
381 if self.pretty_print_methods:
382 self.Emit(
383 ' hnode_t* PrettyTree(bool do_abbrev, Dict<int, bool>* seen = nullptr);'
384 )
385
386 Emit('')
387 Emit(' DISALLOW_COPY_AND_ASSIGN(%(sum_name)s_t)')
388 Emit('};')
389 Emit('')
390
391 for variant in sum.types:
392 if variant.shared_type:
393 continue # Don't generate a class.
394 super_name = '%s_t' % sum_name
395 tag = 'static_cast<uint16_t>(%s_e::%s)' % (sum_name, variant.name)
396 class_name = '%s__%s' % (sum_name, variant.name)
397 self._GenClass(variant.fields, class_name, [super_name], depth,
398 tag)
399
400 # Generate 'extern' declarations for zero arg singleton globals
401 for variant in sum.types:
402 if not variant.shared_type and len(variant.fields) == 0:
403 variant_name = variant.name
404 Emit(
405 'extern GcGlobal<%(sum_name)s__%(variant_name)s> g%(sum_name)s__%(variant_name)s;'
406 )
407
408 # Allow expr::Const in addition to expr.Const.
409 Emit('ASDL_NAMES %(sum_name)s {')
410 for variant in sum.types:
411 if variant.shared_type:
412 continue
413
414 # TODO: This produces a lint error, but IS USED via % reflection
415 variant_name = variant.name
416
417 if len(variant.fields) == 0:
418 Emit(
419 ' static %(sum_name)s__%(variant_name)s* %(variant_name)s;'
420 )
421 else:
422 Emit(
423 ' typedef %(sum_name)s__%(variant_name)s %(variant_name)s;'
424 )
425 Emit('};')
426 Emit('')
427
428 def _GenClassBegin(self, class_name, base_classes, depth):
429 if base_classes:
430 bases = ', '.join('public %s' % b for b in base_classes)
431 self.Emit("class %s : %s {" % (class_name, bases), depth)
432 else:
433 self.Emit("class %s {" % class_name, depth)
434 #self.Emit("class %s : public Walkable {" % class_name, depth)
435
436 self.Emit(" public:", depth)
437
438 def _GenClassEnd(self, class_name, depth):
439 self.Emit(' DISALLOW_COPY_AND_ASSIGN(%s)' % class_name)
440 self.Emit('};', depth)
441 self.Emit('', depth)
442
443 def _EmitMethodsInHeader(self, obj_header_str):
444 """Generate PrettyTree(), type_id(), obj_header()"""
445 if self.pretty_print_methods:
446 self.Emit(
447 'hnode_t* PrettyTree(bool do_abbrev, Dict<int, bool>* seen = nullptr);'
448 )
449 self.Emit('')
450
451 self.Emit('static constexpr ObjHeader obj_header() {')
452 self.Emit(' return %s;' % obj_header_str)
453 self.Emit('}')
454
455 def _GenClassForList(self, class_name, base_classes, tag_num):
456 depth = 0
457 self._GenClassBegin(class_name, base_classes, depth)
458
459 base_type_str = [b for b in base_classes if b.startswith('List<')][0]
460
461 # Zero arg constructor
462 self.Emit(' %s() : %s() {' % (class_name, base_type_str), depth)
463 self.Emit(' }', depth)
464
465 # One arg constructor used by Take.
466 # This should be is PROTECTED, like the superclass, but Alloc<T> calls
467 # it. Hm.
468 #self.Emit(' protected:')
469 self.Emit(
470 ' %s(%s* plain_list) : %s(plain_list) {' %
471 (class_name, base_type_str, base_type_str), depth)
472 self.Emit(' }', depth)
473
474 self.Emit(' static %s* New() {' % class_name, depth)
475 self.Emit(' return Alloc<%s>();' % class_name, depth)
476 self.Emit(' }', depth)
477
478 # Take() constructor
479 self.Emit(
480 ' static %s* Take(%s* plain_list) {' %
481 (class_name, base_type_str), depth)
482 self.Emit(' auto* result = Alloc<%s>(plain_list);' % class_name,
483 depth)
484 self.Emit(' plain_list->SetTaken();', depth)
485 self.Emit(' return result;', depth)
486 self.Emit(' }', depth)
487
488 # field_mask() should call List superclass, since say word_t won't have it
489 obj_header_str = 'ObjHeader::TaggedSubtype(%d, field_mask())' % tag_num
490 self.Indent()
491 self._EmitMethodsInHeader(obj_header_str)
492 self.Dedent()
493
494 self._GenClassEnd(class_name, depth)
495
496 def _GenClass(self,
497 fields,
498 class_name,
499 base_classes,
500 depth,
501 tag_num,
502 obj_header_str=''):
503 """For Product and Constructor."""
504 self._GenClassBegin(class_name, base_classes, depth)
505
506 # Ensure that the member variables are ordered such that GC managed objects
507 # come before any unmanaged ones because we use `HeapTag::Scanned`.
508 managed_fields = []
509 unmanaged_fields = []
510 for f in fields:
511 if _IsManagedType(f.typ):
512 managed_fields.append(f)
513 else:
514 unmanaged_fields.append(f)
515 all_fields = managed_fields + unmanaged_fields
516
517 def FieldInitJoin(strs):
518 # reflow doesn't work well here, so do it manually
519 return ',\n '.join(strs)
520
521 # Ensure that the constructor params are listed in the same order as the
522 # equivalent python constructors for compatibility in translated code.
523 params = []
524 for f in fields:
525 params.append('%s %s' % (_GetCppType(f.typ), f.name))
526
527 # Member initializers are in the same order as the member variables to
528 # avoid compiler warnings (the order doesn't affect the semantics).
529 inits = []
530 for f in all_fields:
531 inits.append('%s(%s)' % (f.name, f.name))
532
533 # Define constructor with N args
534 if len(inits):
535 self.Emit(' %s(%s)' % (class_name, ', '.join(params)), depth)
536 self.Emit(' : %s {' % FieldInitJoin(inits),
537 depth,
538 reflow=False)
539 self.Emit(' }')
540 else:
541 self.Emit(' %s(%s) {}' % (class_name, ', '.join(params)), depth)
542 self.Emit('')
543
544 # Define static constructor with ZERO args. Don't emit for types with no
545 # fields.
546 if fields:
547 init_args = []
548 for field in fields:
549 init_args.append(_DefaultValue(field.typ))
550
551 self.Emit(
552 ' static %s* CreateNull(bool alloc_lists = false) { ' %
553 class_name, depth)
554 self.Emit(
555 ' return Alloc<%s>(%s);' %
556 (class_name, ', '.join(init_args)), depth)
557 self.Emit(' }')
558 self.Emit('')
559
560 obj_header_str = 'ObjHeader::AsdlClass(%s, %d)' % (tag_num,
561 len(managed_fields))
562 self.Indent()
563 self._EmitMethodsInHeader(obj_header_str)
564 self.Dedent()
565
566 #
567 # Members
568 #
569 for field in all_fields:
570 self.Emit(" %s %s;" % (_GetCppType(field.typ), field.name))
571 self.Emit('')
572
573 self._GenClassEnd(class_name, depth)
574
575 def VisitSubType(self, subtype):
576 self._shared_type_tags[subtype.name] = self._product_counter
577
578 # Also create these last. They may inherit from sum types that have yet
579 # to be defined.
580 self._subtypes.append((subtype, self._product_counter))
581 self._product_counter += 1
582
583 def VisitProduct(self, product, name, depth):
584 self._shared_type_tags[name] = self._product_counter
585 # Create a tuple of _GenClass args to create LAST. They may inherit from
586 # sum types that have yet to be defined.
587 self._products.append((product, name, depth, self._product_counter))
588 self._product_counter += 1
589
590 def EmitFooter(self):
591 # Now generate all the product types we deferred.
592 for args in self._products:
593 ast_node, name, depth, tag_num = args
594 # Figure out base classes AFTERWARD.
595 bases = self._base_classes[name]
596 self._GenClass(ast_node.fields, name, bases, depth, tag_num)
597
598 for args in self._subtypes:
599 subtype, tag_num = args
600 # Figure out base classes AFTERWARD.
601 bases = self._base_classes[subtype.name]
602
603 cpp_type = _GetCppType(subtype.base_class)
604 assert cpp_type.endswith('*') # hack
605 cpp_type = cpp_type[:-1]
606 bases.append(cpp_type)
607
608 t = subtype.base_class.type_name
609 if t == 'List':
610 self._GenClassForList(subtype.name, bases, tag_num)
611
612 elif t == 'Dict':
613 raise AssertionError()
614 else:
615 #obj_header_str = ''
616 raise AssertionError()
617
618
619class MethodDefVisitor(visitor.AsdlVisitor):
620 """Generate the body of pretty printing methods.
621
622 We have to do this in another pass because types and schemas have
623 circular dependencies.
624 """
625
626 def __init__(self, f, abbrev_ns=None, abbrev_mod_entries=None):
627 visitor.AsdlVisitor.__init__(self, f)
628 self.abbrev_ns = abbrev_ns
629 self.abbrev_mod_entries = abbrev_mod_entries or []
630
631 def _EmitList(self, list_str, item_type, out_val_name):
632 # used in format strings
633 c_item_type = _GetCppType(item_type)
634
635 def _Emit(s):
636 self.Emit(s % sys._getframe(1).f_locals, self.current_depth)
637
638 _Emit(
639 'hnode::Array* %(out_val_name)s = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());'
640 )
641 _Emit(
642 'for (ListIter<%(c_item_type)s> it(%(list_str)s); !it.Done(); it.Next()) {'
643 )
644 _Emit(' %(c_item_type)s v_ = it.Value();')
645
646 child_code_str, none_guard = _HNodeExpr(item_type, 'v_')
647 if none_guard: # e.g. for List[Optional[value_t]]
648 # TODO: could consolidate this logic with asdl/runtime.py NewLeaf(), which is prebuilt/
649 child_code_str = (
650 '(v_ == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"), color_e::OtherConst) : %s'
651 % child_code_str)
652 _Emit(' hnode_t* h = %(child_code_str)s;')
653 _Emit(' %(out_val_name)s->children->append(h);')
654 _Emit('}')
655
656 def _EmitListPrettyPrint(self, field, out_val_name):
657 typ = field.typ
658 if typ.type_name == 'Optional': # descend one level
659 typ = typ.children[0]
660 item_type = typ.children[0]
661
662 self._EmitList('this->%s' % field.name, item_type, out_val_name)
663
664 def _EmitDictPrettyPrint(self, field):
665 typ = field.typ
666 if typ.type_name == 'Optional': # descend one level
667 typ = typ.children[0]
668
669 k_typ = typ.children[0]
670 v_typ = typ.children[1]
671
672 k_c_type = _GetCppType(k_typ)
673 v_c_type = _GetCppType(v_typ)
674
675 k_code_str, _ = _HNodeExpr(k_typ, 'k')
676 v_code_str, _ = _HNodeExpr(v_typ, 'v')
677
678 self.Emit('auto* unnamed = NewList<hnode_t*>();')
679 self.Emit(
680 'auto* hdict = Alloc<hnode::Record>(kEmptyString, StrFromC("{"), StrFromC("}"), NewList<Field*>(), unnamed);'
681 )
682 self.Emit(
683 'for (DictIter<%s, %s> it(this->%s); !it.Done(); it.Next()) {' %
684 (k_c_type, v_c_type, field.name))
685 self.Emit(' auto k = it.Key();')
686 self.Emit(' auto v = it.Value();')
687 self.Emit(' unnamed->append(%s);' % k_code_str)
688 self.Emit(' unnamed->append(%s);' % v_code_str)
689 self.Emit('}')
690 self.Emit('L->append(Alloc<Field>(StrFromC("%s"), hdict));' %
691 field.name)
692
693 def _EmitCodeForField(self, field, counter):
694 """Generate code that returns an hnode for a field."""
695 out_val_name = 'x%d' % counter
696
697 if ast.IsList(field.typ):
698 self.Emit('if (this->%s != nullptr) { // List' % field.name)
699 self.Indent()
700 self._EmitListPrettyPrint(field, out_val_name)
701 self.Emit('L->append(Alloc<Field>(StrFromC("%s"), %s));' %
702 (field.name, out_val_name))
703 self.Dedent()
704 self.Emit('}')
705
706 elif ast.IsDict(field.typ):
707 self.Emit('if (this->%s != nullptr) { // Dict' % field.name)
708 self.Indent()
709 self._EmitDictPrettyPrint(field)
710 self.Dedent()
711 self.Emit('}')
712
713 elif ast.IsOptional(field.typ):
714 typ = field.typ.children[0]
715
716 self.Emit('if (this->%s) { // Optional' % field.name)
717 child_code_str, _ = _HNodeExpr(typ, 'this->%s' % field.name)
718 self.Emit(' hnode_t* %s = %s;' % (out_val_name, child_code_str))
719 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
720 (field.name, out_val_name))
721 self.Emit('}')
722
723 else:
724 var_name = 'this->%s' % field.name
725 code_str, obj_none_guard = _HNodeExpr(field.typ, var_name)
726
727 depth = self.current_depth
728 if obj_none_guard: # to satisfy MyPy type system
729 pass
730 self.Emit('hnode_t* %s = %s;' % (out_val_name, code_str), depth)
731
732 self.Emit(
733 'L->append(Alloc<Field>(StrFromC("%s"), %s));' %
734 (field.name, out_val_name), depth)
735
736 def _EmitPrettyPrintMethods(self,
737 class_name,
738 all_fields,
739 sum_name=None,
740 list_item_type=None):
741 """
742 """
743 self.Emit('')
744 self.Emit(
745 'hnode_t* %s::PrettyTree(bool do_abbrev, Dict<int, bool>* seen) {'
746 % class_name)
747
748 # Similar to j8::HeapValueId()
749 self.Emit(' seen = seen ? seen : Alloc<Dict<int, bool>>();')
750 self.Emit(' int heap_id = ObjectId(this);')
751 self.Emit(' if (dict_contains(seen, heap_id)) {')
752 self.Emit(' return Alloc<hnode::AlreadySeen>(heap_id);')
753 self.Emit(' }')
754 self.Emit(' seen->set(heap_id, true);')
755 self.Emit('')
756
757 if list_item_type:
758 self.Indent()
759 self._EmitList('this', list_item_type, 'out_node')
760 self.Dedent()
761 else:
762 if sum_name is not None:
763 n = '%s_str(this->tag())' % sum_name
764 else:
765 n = 'StrFromC("%s")' % class_name
766
767 abbrev_name = '_%s' % class_name
768
769 if abbrev_name in self.abbrev_mod_entries:
770 self.Emit(' if (do_abbrev) {')
771 self.Emit(' auto* p = %s::%s(this);' %
772 (self.abbrev_ns, abbrev_name))
773 self.Emit(' if (p) {')
774 self.Emit(' return p;')
775 self.Emit(' }')
776 self.Emit(' }')
777 else:
778 #self.Emit(' // no abbrev %s' % abbrev_name)
779 pass
780
781 self.Emit(' hnode::Record* out_node = runtime::NewRecord(%s);' %
782 n)
783 if all_fields:
784 self.Emit(' List<Field*>* L = out_node->fields;')
785 self.Emit('')
786
787 # Use the runtime type to be more like asdl/format.py
788 for local_id, field in enumerate(all_fields):
789 #log('%s :: %s', field_name, field_desc)
790 self.Indent()
791 self._EmitCodeForField(field, local_id)
792 self.Dedent()
793 self.Emit('')
794 self.Emit(' return out_node;')
795 self.Emit('}')
796 self.Emit('')
797
798 def _EmitStrFunction(self,
799 sum,
800 sum_name,
801 depth,
802 strong=False,
803 simple=False):
804 add_suffix = not ('no_namespace_suffix' in sum.generate)
805 if add_suffix:
806 if simple:
807 enum_name = '%s_i' % sum_name
808 else:
809 enum_name = '%s_e' % sum_name
810 else:
811 enum_name = sum_name
812
813 if strong:
814 self.Emit(
815 'BigStr* %s_str(%s tag, bool dot) {' % (sum_name, enum_name),
816 depth)
817 else:
818 self.Emit('BigStr* %s_str(int tag, bool dot) {' % sum_name, depth)
819
820 buf_size = 32
821 v_max = max(len(variant.name) for variant in sum.types)
822 s_max = v_max + 1 + len(sum_name) + 1 # for . and NUL
823 if s_max > buf_size:
824 raise RuntimeError('Sum name %r + variant name is too long' %
825 sum_name)
826
827 self.Emit(' char buf[%d];' % buf_size, depth)
828 self.Emit(' const char* v = nullptr;', depth)
829 self.Emit(' switch (tag) {', depth)
830 for variant in sum.types:
831 self.Emit('case %s::%s:' % (enum_name, variant.name), depth + 1)
832 self.Emit(' v = "%s"; break;' % variant.name, depth + 1)
833
834 self.Emit('default:', depth + 1)
835 self.Emit(' assert(0);', depth + 1)
836
837 self.Emit(' }', depth)
838 self.Emit(' if (dot) {', depth)
839 self.Emit(' snprintf(buf, %d, "%s.%%s", v);' % (buf_size, sum_name),
840 depth)
841 self.Emit(' return StrFromC(buf);', depth)
842 self.Emit(' } else {', depth)
843 self.Emit(' return StrFromC(v);', depth)
844 self.Emit(' }', depth)
845 self.Emit('}', depth)
846
847 def VisitSimpleSum(self, sum, name, depth):
848 if 'integers' in sum.generate or 'uint16' in sum.generate:
849 self._EmitStrFunction(sum, name, depth, strong=False, simple=True)
850 else:
851 self._EmitStrFunction(sum, name, depth, strong=True)
852
853 def VisitCompoundSum(self, sum, sum_name, depth):
854 self._EmitStrFunction(sum, sum_name, depth)
855
856 # Generate definitions for the for zero arg singleton globals
857 for variant in sum.types:
858 if variant.shared_type:
859 continue
860 if len(variant.fields) == 0:
861 variant_name = variant.name
862 self.Emit('')
863 self.Emit('%s__%s* %s::%s = &g%s__%s.obj;' %
864 (sum_name, variant_name, sum_name, variant_name,
865 sum_name, variant_name))
866 self.Emit('')
867 self.Emit('GcGlobal<%s__%s> g%s__%s = ' %
868 (sum_name, variant_name, sum_name, variant_name))
869 self.Emit(' { ObjHeader::Global(%s_e::%s) };' %
870 (sum_name, variant_name))
871
872 for variant in sum.types:
873 if variant.shared_type:
874 continue
875 all_fields = variant.fields
876 class_name = '%s__%s' % (sum_name, variant.name)
877 self._EmitPrettyPrintMethods(class_name,
878 all_fields,
879 sum_name=sum_name)
880
881 # Emit dispatch WITHOUT using 'virtual'
882 self.Emit('')
883 self.Emit(
884 'hnode_t* %s_t::PrettyTree(bool do_abbrev, Dict<int, bool>* seen) {'
885 % sum_name)
886 self.Emit(' switch (this->tag()) {', depth)
887
888 for variant in sum.types:
889 if variant.shared_type:
890 subtype_name = variant.shared_type
891 else:
892 subtype_name = '%s__%s' % (sum_name, variant.name)
893
894 self.Emit(' case %s_e::%s: {' % (sum_name, variant.name), depth)
895 self.Emit(
896 ' %s* obj = static_cast<%s*>(this);' %
897 (subtype_name, subtype_name), depth)
898 self.Emit(' return obj->PrettyTree(do_abbrev, seen);', depth)
899 self.Emit(' }', depth)
900
901 self.Emit(' default:', depth)
902 self.Emit(' assert(0);', depth)
903
904 self.Emit(' }')
905 self.Emit('}')
906
907 def VisitProduct(self, product, name, depth):
908 self._EmitPrettyPrintMethods(name, product.fields)
909
910 def VisitSubType(self, subtype):
911 list_item_type = None
912 b = subtype.base_class
913 if isinstance(b, ast.ParameterizedType):
914 if b.type_name == 'List':
915 list_item_type = b.children[0]
916 self._EmitPrettyPrintMethods(subtype.name, [],
917 list_item_type=list_item_type)