| 1 | """
 | 
| 2 | gen_cpp.py - Generate C++ classes from an ASDL schema.
 | 
| 3 | 
 | 
| 4 | TODO:
 | 
| 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 | """
 | 
| 21 | from __future__ import print_function
 | 
| 22 | 
 | 
| 23 | import sys
 | 
| 24 | 
 | 
| 25 | from collections import defaultdict
 | 
| 26 | 
 | 
| 27 | from asdl import ast
 | 
| 28 | from asdl import visitor
 | 
| 29 | from asdl.util import log
 | 
| 30 | 
 | 
| 31 | _ = log
 | 
| 32 | 
 | 
| 33 | # Not supporting concise syntax tree like Python
 | 
| 34 | ABBREV = False
 | 
| 35 | 
 | 
| 36 | if ABBREV:
 | 
| 37 |     PRETTY_METHODS = ['PrettyTree', '_AbbreviatedTree', 'AbbreviatedTree']
 | 
| 38 | else:
 | 
| 39 |     PRETTY_METHODS = ['PrettyTree']
 | 
| 40 | 
 | 
| 41 | 
 | 
| 42 | # Used by core/asdl_gen.py to generate _devbuild/gen/osh-types.h, with
 | 
| 43 | # lex_mode__*
 | 
| 44 | class 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 | 
 | 
| 67 | class 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 | 
 | 
| 83 | def _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 | 
 | 
| 120 | def _IsManagedType(typ):
 | 
| 121 |     # This is a little cheesy, but works
 | 
| 122 |     return _GetCppType(typ).endswith('*')
 | 
| 123 | 
 | 
| 124 | 
 | 
| 125 | def _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 | 
 | 
| 177 | def _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 | 
 | 
| 227 | class 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 | 
 | 
| 521 | class 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)
 |