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

826 lines, 554 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._product_bases = defaultdict(list)
245
246 def _EmitEnum(self, sum, sum_name, depth, strong=False, is_simple=False):
247 enum = []
248 int_to_type = {}
249 add_suffix = not ('no_namespace_suffix' in sum.generate)
250 for i, variant in enumerate(sum.types):
251 if variant.shared_type: # Copied from gen_python.py
252 tag_num = self._shared_type_tags[variant.shared_type]
253 # e.g. DoubleQuoted may have base types expr_t, word_part_t
254 base_class = sum_name + '_t'
255 bases = self._product_bases[variant.shared_type]
256 if base_class in bases:
257 raise RuntimeError(
258 "Two tags in sum %r refer to product type %r" %
259 (sum_name, variant.shared_type))
260 else:
261 bases.append(base_class)
262 type_str = variant.shared_type
263 else:
264 tag_num = i + 1
265 type_str = '%s__%s' % (sum_name, variant.name)
266 int_to_type[tag_num] = type_str
267 enum.append((variant.name, tag_num)) # zero is reserved
268
269 if strong:
270 enum_name = '%s_e' % sum_name if add_suffix else sum_name
271
272 # Simple sum types can be STRONG since there's no possibility of multiple
273 # inheritance!
274
275 self.Emit('enum class %s {' % enum_name, depth)
276 for name, tag_num in enum:
277 self.Emit('%s = %d,' % (name, tag_num), depth + 1)
278 self.Emit('};', depth)
279
280 # type alias to match Python code
281 self.Emit('typedef %s %s_t;' % (enum_name, sum_name), depth)
282 self.Emit('', depth)
283
284 if self.pretty_print_methods:
285 self.Emit(
286 'BigStr* %s_str(%s tag, bool dot = true);' %
287 (sum_name, enum_name), depth)
288 self.Emit('', depth)
289
290 else:
291 if is_simple:
292 enum_name = '%s_i' % sum_name if add_suffix else sum_name
293 else:
294 enum_name = '%s_e' % sum_name if add_suffix else sum_name
295
296 # Awkward struct/enum C++ idiom because:
297
298 # 1. namespace can't be "imported" with 'using'
299 # 2. plain enum pollutes outer namespace
300 # 3. C++ 11 'enum class' does not allow conversion to int
301 # 4. namespace and 'static const int' or 'static constexpr int' gives
302 # weird link errors
303 # https://quuxplusone.github.io/blog/2020/09/19/value-or-pitfall/
304
305 self.Emit('ASDL_NAMES %s {' % enum_name, depth)
306 self.Emit(' enum no_name {', depth)
307 for name, tag_num in enum:
308 self.Emit('%s = %d,' % (name, tag_num), depth + 1)
309
310 if is_simple:
311 # Help in sizing array. Note that we're 1-based.
312 self.Emit('ARRAY_SIZE = %d,' % (len(enum) + 1), depth + 1)
313
314 self.Emit(' };', depth)
315 self.Emit('};', depth)
316
317 self.Emit('', depth)
318
319 if self.pretty_print_methods:
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(
347 sum.types) < 64, '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 # Can't be constructed directly. Note: this shows up in uftrace in debug
361 # mode, e.g. when we instantiate Token. Do we need it?
362 Emit(' protected:')
363 Emit(' %s_t() {' % sum_name)
364 Emit(' }')
365 Emit(' public:')
366 Emit(' int tag() const {')
367 # There's no inheritance relationship, so we have to reinterpret_cast.
368 Emit(' return ObjHeader::FromObject(this)->type_tag;')
369 Emit(' }')
370
371 if self.pretty_print_methods:
372 for abbrev in PRETTY_METHODS:
373 self.Emit(' hnode_t* %s(Dict<int, bool>* seen = nullptr);' %
374 abbrev)
375
376 Emit(' DISALLOW_COPY_AND_ASSIGN(%(sum_name)s_t)')
377 Emit('};')
378 Emit('')
379
380 for variant in sum.types:
381 if variant.shared_type:
382 # Don't generate a class.
383 pass
384 else:
385 super_name = '%s_t' % sum_name
386 tag = 'static_cast<uint16_t>(%s_e::%s)' % (sum_name,
387 variant.name)
388 class_name = '%s__%s' % (sum_name, variant.name)
389 self._GenClass(variant, class_name, [super_name], depth, tag)
390
391 # Generate 'extern' declarations for zero arg singleton globals
392 for variant in sum.types:
393 if not variant.shared_type and len(variant.fields) == 0:
394 variant_name = variant.name
395 Emit(
396 'extern GcGlobal<%(sum_name)s__%(variant_name)s> g%(sum_name)s__%(variant_name)s;'
397 )
398
399 # Allow expr::Const in addition to expr.Const.
400 Emit('ASDL_NAMES %(sum_name)s {')
401 for variant in sum.types:
402 if variant.shared_type:
403 continue
404
405 # TODO: This produces a lint error, but IS USED via % reflection
406 variant_name = variant.name
407
408 if len(variant.fields) == 0:
409 Emit(
410 ' static %(sum_name)s__%(variant_name)s* %(variant_name)s;'
411 )
412 else:
413 Emit(
414 ' typedef %(sum_name)s__%(variant_name)s %(variant_name)s;'
415 )
416 Emit('};')
417 Emit('')
418
419 def _GenClass(self, ast_node, class_name, base_classes, depth, tag):
420 """For Product and Constructor."""
421 if base_classes:
422 bases = ', '.join('public %s' % b for b in base_classes)
423 self.Emit("class %s : %s {" % (class_name, bases), depth)
424 else:
425 self.Emit("class %s {" % class_name, depth)
426 self.Emit(" public:", depth)
427
428 # Ensure that the member variables are ordered such that GC managed objects
429 # come before any unmanaged ones because we use `HeapTag::Scanned`.
430 managed_fields, unmanaged_fields = [], []
431 for f in ast_node.fields:
432 if _IsManagedType(f.typ):
433 managed_fields.append(f)
434 else:
435 unmanaged_fields.append(f)
436 all_fields = managed_fields + unmanaged_fields
437
438 def FieldInitJoin(strs):
439 # reflow doesn't work well here, so do it manually
440 return ',\n '.join(strs)
441
442 # Ensure that the constructor params are listed in the same order as the
443 # equivalent python constructors for compatibility in translated code.
444 params = []
445 for f in ast_node.fields:
446 params.append('%s %s' % (_GetCppType(f.typ), f.name))
447
448 # Member initializers are in the same order as the member variables to
449 # avoid compiler warnings (the order doesn't affect the semantics).
450 inits = []
451 for f in all_fields:
452 inits.append('%s(%s)' % (f.name, f.name))
453
454 # Define constructor with N args
455 if len(inits):
456 self.Emit(' %s(%s)' % (class_name, ', '.join(params)), depth)
457 self.Emit(' : %s {' % FieldInitJoin(inits),
458 depth,
459 reflow=False)
460 self.Emit(' }')
461 else:
462 self.Emit(' %s(%s) {}' % (class_name, ', '.join(params)), depth)
463 self.Emit('')
464
465 # Define static constructor with ZERO args. Don't emit for types with no
466 # fields.
467 if ast_node.fields:
468 init_args = []
469 for field in ast_node.fields:
470 init_args.append(_DefaultValue(field.typ))
471
472 self.Emit(
473 ' static %s* CreateNull(bool alloc_lists = false) { ' %
474 class_name, depth)
475 self.Emit(
476 ' return Alloc<%s>(%s);' %
477 (class_name, ', '.join(init_args)), depth)
478 self.Emit(' }')
479 self.Emit('')
480
481 if self.pretty_print_methods:
482 for abbrev in PRETTY_METHODS:
483 self.Emit(
484 ' hnode_t* %s(Dict<int, bool>* seen = nullptr);' % abbrev,
485 depth)
486 self.Emit('')
487
488 self.Emit(' static constexpr ObjHeader obj_header() {')
489 self.Emit(' return ObjHeader::AsdlClass(%s, %d);' %
490 (tag, len(managed_fields)))
491 self.Emit(' }')
492 self.Emit('')
493
494 #
495 # Members
496 #
497 for field in all_fields:
498 self.Emit(" %s %s;" % (_GetCppType(field.typ), field.name))
499
500 self.Emit('')
501 self.Emit(' DISALLOW_COPY_AND_ASSIGN(%s)' % class_name)
502 self.Emit('};', depth)
503 self.Emit('', depth)
504
505 def VisitProduct(self, product, name, depth):
506 self._shared_type_tags[name] = self._product_counter
507 # Create a tuple of _GenClass args to create LAST. They may inherit from
508 # sum types that have yet to be defined.
509 self._products.append((product, name, depth, self._product_counter))
510 self._product_counter += 1
511
512 def EmitFooter(self):
513 # Now generate all the product types we deferred.
514 for args in self._products:
515 ast_node, name, depth, tag_num = args
516 # Figure out base classes AFTERWARD.
517 bases = self._product_bases[name]
518 self._GenClass(ast_node, name, bases, depth, tag_num)
519
520
521class MethodDefVisitor(visitor.AsdlVisitor):
522 """Generate the body of pretty printing methods.
523
524 We have to do this in another pass because types and schemas have
525 circular dependencies.
526 """
527
528 def __init__(self, f, pretty_print_methods=True):
529 visitor.AsdlVisitor.__init__(self, f)
530
531 def _EmitCodeForField(self, abbrev, field, counter):
532 """Generate code that returns an hnode for a field."""
533 out_val_name = 'x%d' % counter
534
535 if field.typ.IsList():
536 iter_name = 'i%d' % counter
537
538 typ = field.typ
539 if typ.type_name == 'Optional': # descend one level
540 typ = typ.children[0]
541 item_type = typ.children[0]
542
543 self.Emit('if (this->%s != nullptr) { // List' % (field.name))
544 self.Emit(
545 ' hnode::Array* %s = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());'
546 % out_val_name)
547 c_item_type = _GetCppType(item_type)
548 self.Emit(
549 ' for (ListIter<%s> it(this->%s); !it.Done(); it.Next()) {' %
550 (c_item_type, field.name))
551 self.Emit(' %s %s = it.Value();' % (c_item_type, iter_name))
552
553 child_code_str, none_guard = _HNodeExpr(abbrev, item_type,
554 iter_name)
555 if none_guard: # e.g. for List[Optional[value_t]]
556 # TODO: could consolidate with asdl/runtime.py NewLeaf(), which
557 # also uses _ to mean None/nullptr
558 self.Emit(
559 ' hnode_t* h = (%s == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"), color_e::OtherConst) : %s;'
560 % (iter_name, child_code_str))
561 self.Emit(' %s->children->append(h);' % out_val_name)
562 else:
563 self.Emit(' %s->children->append(%s);' %
564 (out_val_name, child_code_str))
565
566 self.Emit(' }')
567 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
568 (field.name, out_val_name))
569 self.Emit('}')
570
571 elif field.typ.IsDict():
572 k = 'k%d' % counter
573 v = 'v%d' % counter
574
575 typ = field.typ
576 if typ.type_name == 'Optional': # descend one level
577 typ = typ.children[0]
578
579 k_typ = typ.children[0]
580 v_typ = typ.children[1]
581
582 k_c_type = _GetCppType(k_typ)
583 v_c_type = _GetCppType(v_typ)
584
585 k_code_str, _ = _HNodeExpr(abbrev, k_typ, k)
586 v_code_str, _ = _HNodeExpr(abbrev, v_typ, v)
587
588 self.Emit('if (this->%s) { // Dict' % field.name)
589 # TODO: m can be a global constant!
590 self.Emit(
591 ' auto m = Alloc<hnode::Leaf>(StrFromC("Dict"), color_e::OtherConst);'
592 )
593 self.Emit(
594 ' hnode::Array* %s = Alloc<hnode::Array>(NewList<hnode_t*>({m}));'
595 % out_val_name)
596 self.Emit(
597 ' for (DictIter<%s, %s> it(this->%s); !it.Done(); it.Next()) {'
598 % (k_c_type, v_c_type, field.name))
599 self.Emit(' auto %s = it.Key();' % k)
600 self.Emit(' auto %s = it.Value();' % v)
601 self.Emit(' %s->children->append(%s);' %
602 (out_val_name, k_code_str))
603 self.Emit(' %s->children->append(%s);' %
604 (out_val_name, v_code_str))
605 self.Emit(' }')
606 self.Emit(' L->append(Alloc<Field>(StrFromC ("%s"), %s));' %
607 (field.name, out_val_name))
608 self.Emit('}')
609
610 elif field.typ.IsOptional():
611 typ = field.typ.children[0]
612
613 self.Emit('if (this->%s) { // Optional' % field.name)
614 child_code_str, _ = _HNodeExpr(abbrev, typ,
615 'this->%s' % field.name)
616 self.Emit(' hnode_t* %s = %s;' % (out_val_name, child_code_str))
617 self.Emit(' L->append(Alloc<Field>(StrFromC("%s"), %s));' %
618 (field.name, out_val_name))
619 self.Emit('}')
620
621 else:
622 var_name = 'this->%s' % field.name
623 code_str, obj_none_guard = _HNodeExpr(abbrev, field.typ, var_name)
624
625 depth = self.current_depth
626 if obj_none_guard: # to satisfy MyPy type system
627 pass
628 self.Emit('hnode_t* %s = %s;' % (out_val_name, code_str), depth)
629
630 self.Emit(
631 'L->append(Alloc<Field>(StrFromC("%s"), %s));' %
632 (field.name, out_val_name), depth)
633
634 def _EmitPrettyPrintMethods(self,
635 class_name,
636 all_fields,
637 ast_node,
638 sum_name=None):
639 #
640 # PrettyTree
641 #
642
643 if sum_name is not None:
644 n = '%s_str(this->tag())' % sum_name
645 else:
646 n = 'StrFromC("%s")' % class_name
647
648 self.Emit('')
649 self.Emit('hnode_t* %s::PrettyTree(Dict<int, bool>* seen) {' %
650 class_name)
651
652 # Similar to j8::HeapValueId()
653 self.Emit(' seen = seen ? seen : Alloc<Dict<int, bool>>();')
654 self.Emit(' int heap_id = ObjectId(this);')
655 self.Emit(' if (dict_contains(seen, heap_id)) {')
656 self.Emit(' return Alloc<hnode::AlreadySeen>(heap_id);')
657 self.Emit(' }')
658 self.Emit(' seen->set(heap_id, true);')
659
660 self.Emit(' hnode::Record* out_node = runtime::NewRecord(%s);' % n)
661 if all_fields:
662 self.Emit(' List<Field*>* L = out_node->fields;')
663 self.Emit('')
664
665 # Use the runtime type to be more like asdl/format.py
666 for local_id, field in enumerate(all_fields):
667 #log('%s :: %s', field_name, field_desc)
668 self.Indent()
669 self._EmitCodeForField('PrettyTree', field, local_id)
670 self.Dedent()
671 self.Emit('')
672 self.Emit(' return out_node;')
673 self.Emit('}')
674 self.Emit('')
675
676 #
677 # _AbbreviatedTree
678 #
679
680 if not ABBREV:
681 return
682
683 self.Emit('')
684 self.Emit('hnode_t* %s::_AbbreviatedTree() {' % class_name)
685 self.Emit(' hnode::Record* out_node = runtime::NewRecord("%s");' % n)
686 if ast_node.fields:
687 self.Emit(' List<Field*>* L = out_node->fields;')
688
689 for local_id, field in enumerate(ast_node.fields):
690 self.Indent()
691 self._EmitCodeForField('AbbreviatedTree', field, local_id)
692 self.Dedent()
693 self.Emit('')
694 self.Emit(' return out_node;')
695 self.Emit('}')
696 self.Emit('')
697
698 self.Emit('hnode_t* %s::AbbreviatedTree() {' % class_name)
699 abbrev_name = '_%s' % class_name
700
701 # STUB
702 self.abbrev_mod_entries = []
703
704 if abbrev_name in self.abbrev_mod_entries:
705 self.Emit(' hnode_t* p = %s();' % abbrev_name)
706 # If the user function didn't return anything, fall back.
707 self.Emit(' return p ? p : _AbbreviatedTree();')
708 else:
709 self.Emit(' return _AbbreviatedTree();')
710 self.Emit('}')
711
712 def _EmitStrFunction(self,
713 sum,
714 sum_name,
715 depth,
716 strong=False,
717 simple=False):
718 add_suffix = not ('no_namespace_suffix' in sum.generate)
719 if add_suffix:
720 if simple:
721 enum_name = '%s_i' % sum_name
722 else:
723 enum_name = '%s_e' % sum_name
724 else:
725 enum_name = sum_name
726
727 if strong:
728 self.Emit(
729 'BigStr* %s_str(%s tag, bool dot) {' % (sum_name, enum_name),
730 depth)
731 else:
732 self.Emit('BigStr* %s_str(int tag, bool dot) {' % sum_name, depth)
733
734 buf_size = 32
735 v_max = max(len(variant.name) for variant in sum.types)
736 s_max = v_max + 1 + len(sum_name) + 1 # for . and NUL
737 if s_max > buf_size:
738 raise RuntimeError('Sum name %r + variant name is too long' %
739 sum_name)
740
741 self.Emit(' char buf[%d];' % buf_size, depth)
742 self.Emit(' const char* v = nullptr;', depth)
743 self.Emit(' switch (tag) {', depth)
744 for variant in sum.types:
745 self.Emit('case %s::%s:' % (enum_name, variant.name), depth + 1)
746 self.Emit(' v = "%s"; break;' % variant.name, depth + 1)
747
748 self.Emit('default:', depth + 1)
749 self.Emit(' assert(0);', depth + 1)
750
751 self.Emit(' }', depth)
752 self.Emit(' if (dot) {', depth)
753 self.Emit(' snprintf(buf, %d, "%s.%%s", v);' % (buf_size, sum_name),
754 depth)
755 self.Emit(' return StrFromC(buf);', depth)
756 self.Emit(' } else {', depth)
757 self.Emit(' return StrFromC(v);', depth)
758 self.Emit(' }', depth)
759 self.Emit('}', depth)
760
761 def VisitSimpleSum(self, sum, name, depth):
762 if 'integers' in sum.generate or 'uint16' in sum.generate:
763 self._EmitStrFunction(sum, name, depth, strong=False, simple=True)
764 else:
765 self._EmitStrFunction(sum, name, depth, strong=True)
766
767 def VisitCompoundSum(self, sum, sum_name, depth):
768 self._EmitStrFunction(sum, sum_name, depth)
769
770 # Generate definitions for the for zero arg singleton globals
771 for variant in sum.types:
772 if variant.shared_type:
773 continue
774 if len(variant.fields) == 0:
775 variant_name = variant.name
776 self.Emit('')
777 self.Emit('%s__%s* %s::%s = &g%s__%s.obj;' %
778 (sum_name, variant_name, sum_name, variant_name,
779 sum_name, variant_name))
780 self.Emit('')
781 self.Emit('GcGlobal<%s__%s> g%s__%s = ' %
782 (sum_name, variant_name, sum_name, variant_name))
783 self.Emit(' { ObjHeader::Global(%s_e::%s) };' %
784 (sum_name, variant_name))
785
786 for variant in sum.types:
787 if variant.shared_type:
788 continue
789 all_fields = variant.fields
790 class_name = '%s__%s' % (sum_name, variant.name)
791 self._EmitPrettyPrintMethods(class_name,
792 all_fields,
793 variant,
794 sum_name=sum_name)
795
796 # Emit dispatch WITHOUT using 'virtual'
797 for func_name in PRETTY_METHODS:
798 self.Emit('')
799 self.Emit('hnode_t* %s_t::%s(Dict<int, bool>* seen) {' %
800 (sum_name, func_name))
801 self.Emit(' switch (this->tag()) {', depth)
802
803 for variant in sum.types:
804 if variant.shared_type:
805 subtype_name = variant.shared_type
806 else:
807 subtype_name = '%s__%s' % (sum_name, variant.name)
808
809 self.Emit(' case %s_e::%s: {' % (sum_name, variant.name),
810 depth)
811 self.Emit(
812 ' %s* obj = static_cast<%s*>(this);' %
813 (subtype_name, subtype_name), depth)
814 self.Emit(' return obj->%s(seen);' % func_name, depth)
815 self.Emit(' }', depth)
816
817 self.Emit(' default:', depth)
818 self.Emit(' assert(0);', depth)
819
820 self.Emit(' }')
821 self.Emit('}')
822
823 def VisitProduct(self, product, name, depth):
824 #self._GenClass(product, product.attributes, name, None, depth)
825 all_fields = product.fields
826 self._EmitPrettyPrintMethods(name, all_fields, product)