| 1 | """
|
| 2 | cppgen_pass.py - AST pass that prints C++ code
|
| 3 | """
|
| 4 | import itertools
|
| 5 | import json # for "C escaping"
|
| 6 |
|
| 7 | from typing import Union, Optional, Dict
|
| 8 |
|
| 9 | import mypy
|
| 10 | from mycpp import visitor
|
| 11 | from mypy.types import (Type, AnyType, NoneTyp, TupleType, Instance,
|
| 12 | Overloaded, CallableType, UnionType, UninhabitedType,
|
| 13 | PartialType, TypeAliasType)
|
| 14 | from mypy.nodes import (Expression, Statement, NameExpr, IndexExpr, MemberExpr,
|
| 15 | TupleExpr, ExpressionStmt, IfStmt, StrExpr, SliceExpr,
|
| 16 | FuncDef, UnaryExpr, OpExpr, CallExpr, ListExpr,
|
| 17 | DictExpr, ClassDef, ForStmt, AssignmentStmt)
|
| 18 |
|
| 19 | from mycpp import format_strings
|
| 20 | from mycpp import pass_state
|
| 21 | from mycpp import util
|
| 22 | from mycpp.util import log, SymbolToString, SplitPyName
|
| 23 |
|
| 24 | from typing import Tuple, List, Any, TYPE_CHECKING
|
| 25 | if TYPE_CHECKING:
|
| 26 | from mycpp import const_pass
|
| 27 | from mycpp import conversion_pass
|
| 28 |
|
| 29 |
|
| 30 | def _IsContextManager(class_name: util.SymbolPath) -> bool:
|
| 31 | return class_name[-1].startswith('ctx_')
|
| 32 |
|
| 33 |
|
| 34 | def _GetCTypeForCast(type_expr: Expression) -> str:
|
| 35 | """ MyPy cast() """
|
| 36 |
|
| 37 | if isinstance(type_expr, MemberExpr):
|
| 38 | left = type_expr.expr
|
| 39 | assert isinstance(left, NameExpr), left # assume it's module.Type
|
| 40 | subtype_name = '%s::%s' % (left.name, type_expr.name)
|
| 41 | elif isinstance(type_expr, IndexExpr):
|
| 42 | # List[word_t] would be a problem.
|
| 43 | # But worked around it in osh/word_parse.py
|
| 44 | #subtype_name = 'List<word_t>'
|
| 45 | raise AssertionError()
|
| 46 | elif isinstance(type_expr, StrExpr):
|
| 47 | parts = type_expr.value.split('.')
|
| 48 | subtype_name = '::'.join(parts)
|
| 49 | elif isinstance(type_expr, NameExpr):
|
| 50 | subtype_name = type_expr.name
|
| 51 | else:
|
| 52 | raise AssertionError()
|
| 53 |
|
| 54 | # Hack for now
|
| 55 | if subtype_name != 'int' and subtype_name != 'mops::BigInt':
|
| 56 | subtype_name += '*'
|
| 57 | return subtype_name
|
| 58 |
|
| 59 |
|
| 60 | def _GetCastKind(module_path: str, cast_to_type: str) -> str:
|
| 61 | """Translate MyPy cast to C++ cast.
|
| 62 |
|
| 63 | Prefer static_cast, but sometimes we need reinterpret_cast.
|
| 64 | """
|
| 65 | cast_kind = 'static_cast'
|
| 66 |
|
| 67 | # Hack for Id.Expr_CastedDummy in expr_to_ast.py
|
| 68 | if 'expr_to_ast.py' in module_path:
|
| 69 | for name in (
|
| 70 | 'ShArrayLiteral',
|
| 71 | 'CommandSub',
|
| 72 | 'BracedVarSub',
|
| 73 | 'DoubleQuoted',
|
| 74 | 'SingleQuoted',
|
| 75 | # Another kind of hack, not because of CastDummy
|
| 76 | 'y_lhs_t',
|
| 77 | ):
|
| 78 | if name in cast_to_type:
|
| 79 | cast_kind = 'reinterpret_cast'
|
| 80 | break
|
| 81 |
|
| 82 | # The other side of Id.Expr_CastedDummy
|
| 83 | if 'expr_parse.py' in module_path:
|
| 84 | for name in ('Token', ):
|
| 85 | if name in cast_to_type:
|
| 86 | cast_kind = 'reinterpret_cast'
|
| 87 | break
|
| 88 |
|
| 89 | if 'process.py' in module_path and 'mylib::Writer' in cast_to_type:
|
| 90 | cast_kind = 'reinterpret_cast'
|
| 91 |
|
| 92 | return cast_kind
|
| 93 |
|
| 94 |
|
| 95 | def _ContainsFunc(t: Type) -> Optional[str]:
|
| 96 | """ x in y """
|
| 97 | contains_func = None
|
| 98 |
|
| 99 | if isinstance(t, Instance):
|
| 100 | type_name = t.type.fullname
|
| 101 |
|
| 102 | if type_name == 'builtins.list':
|
| 103 | contains_func = 'list_contains'
|
| 104 |
|
| 105 | elif type_name == 'builtins.str':
|
| 106 | contains_func = 'str_contains'
|
| 107 |
|
| 108 | elif type_name == 'builtins.dict':
|
| 109 | contains_func = 'dict_contains'
|
| 110 |
|
| 111 | elif isinstance(t, UnionType):
|
| 112 | # Special case for Optional[T] == Union[T, None]
|
| 113 | if len(t.items) != 2:
|
| 114 | raise NotImplementedError('Expected Optional, got %s' % t)
|
| 115 |
|
| 116 | if not isinstance(t.items[1], NoneTyp):
|
| 117 | raise NotImplementedError('Expected Optional, got %s' % t)
|
| 118 |
|
| 119 | contains_func = _ContainsFunc(t.items[0])
|
| 120 |
|
| 121 | return contains_func # None checked later
|
| 122 |
|
| 123 |
|
| 124 | def _EqualsFunc(left_type: Type) -> Optional[str]:
|
| 125 | if util.IsStr(left_type):
|
| 126 | return 'str_equals'
|
| 127 |
|
| 128 | if (isinstance(left_type, UnionType) and len(left_type.items) == 2 and
|
| 129 | util.IsStr(left_type.items[0]) and
|
| 130 | isinstance(left_type.items[1], NoneTyp)):
|
| 131 | return 'maybe_str_equals'
|
| 132 |
|
| 133 | return None
|
| 134 |
|
| 135 |
|
| 136 | _EXPLICIT = ('builtins.str', 'builtins.list', 'builtins.dict')
|
| 137 |
|
| 138 |
|
| 139 | def _CheckCondition(node: Expression, types: Dict[Expression, Type]) -> bool:
|
| 140 | """
|
| 141 | Ban
|
| 142 | if (mystr)
|
| 143 | if (mylist)
|
| 144 | if (mydict)
|
| 145 |
|
| 146 | They mean non-empty in Python.
|
| 147 | """
|
| 148 | #log('NODE %s', node)
|
| 149 |
|
| 150 | if isinstance(node, UnaryExpr) and node.op == 'not':
|
| 151 | return _CheckCondition(node.expr, types)
|
| 152 |
|
| 153 | if isinstance(node, OpExpr):
|
| 154 | #log('OpExpr node %s %s', node, dir(node))
|
| 155 |
|
| 156 | # if x > 0 and not mylist, etc.
|
| 157 | return (_CheckCondition(node.left, types) and
|
| 158 | _CheckCondition(node.right, types))
|
| 159 |
|
| 160 | t = types[node]
|
| 161 |
|
| 162 | if isinstance(t, Instance):
|
| 163 | type_name = t.type.fullname
|
| 164 | if type_name in _EXPLICIT:
|
| 165 | return False
|
| 166 |
|
| 167 | elif isinstance(t, UnionType):
|
| 168 | if len(t.items) == 2 and isinstance(t.items[1], NoneTyp):
|
| 169 | t2 = t.items[0]
|
| 170 | assert isinstance(t2, Instance), t2
|
| 171 | if t2.type.fullname in _EXPLICIT:
|
| 172 | return False
|
| 173 |
|
| 174 | return True
|
| 175 |
|
| 176 |
|
| 177 | def CTypeIsManaged(c_type: str) -> bool:
|
| 178 | """For rooting and field masks."""
|
| 179 | assert c_type != 'void'
|
| 180 |
|
| 181 | if util.SMALL_STR:
|
| 182 | if c_type == 'Str':
|
| 183 | return True
|
| 184 |
|
| 185 | # int, double, bool, scope_t enums, etc. are not managed
|
| 186 | return c_type.endswith('*')
|
| 187 |
|
| 188 |
|
| 189 | def GetCType(t: Type) -> str:
|
| 190 | """Recursively translate MyPy type to C++ type."""
|
| 191 | is_pointer = False
|
| 192 |
|
| 193 | if isinstance(t, UninhabitedType):
|
| 194 | # UninhabitedType is used by def e_usage() -> NoReturn
|
| 195 | # TODO: we could add [[noreturn]] here!
|
| 196 | c_type = 'void'
|
| 197 |
|
| 198 | elif isinstance(t, PartialType):
|
| 199 | # I removed the last instance of this! It was dead code in comp_ui.py.
|
| 200 | raise AssertionError()
|
| 201 | #c_type = 'void'
|
| 202 | #is_pointer = True
|
| 203 |
|
| 204 | elif isinstance(t,
|
| 205 | NoneTyp): # e.g. a function that doesn't return anything
|
| 206 | return 'void'
|
| 207 |
|
| 208 | elif isinstance(t, AnyType):
|
| 209 | # 'any' in ASDL becomes void*
|
| 210 | # It's useful for value::BuiltinFunc(void* f) which is a vm::_Callable*
|
| 211 | c_type = 'void'
|
| 212 | is_pointer = True
|
| 213 |
|
| 214 | elif isinstance(t, CallableType):
|
| 215 | # Function types are expanded
|
| 216 | # Callable[[Parser, Token, int], arith_expr_t]
|
| 217 | # -> arith_expr_t* (*f)(Parser*, Token*, int) nud;
|
| 218 |
|
| 219 | ret_type = GetCType(t.ret_type)
|
| 220 | arg_types = [GetCType(typ) for typ in t.arg_types]
|
| 221 | c_type = '%s (*f)(%s)' % (ret_type, ', '.join(arg_types))
|
| 222 |
|
| 223 | elif isinstance(t, TypeAliasType):
|
| 224 | if 0:
|
| 225 | log('***')
|
| 226 | log('%s', t)
|
| 227 | log('%s', dir(t))
|
| 228 | log('%s', t.alias)
|
| 229 | log('%s', dir(t.alias))
|
| 230 | log('%s', t.alias.target)
|
| 231 | log('***')
|
| 232 | return GetCType(t.alias.target)
|
| 233 |
|
| 234 | elif isinstance(t, Instance):
|
| 235 | type_name = t.type.fullname
|
| 236 | #log('** TYPE NAME %s', type_name)
|
| 237 |
|
| 238 | if type_name == 'builtins.int':
|
| 239 | c_type = 'int'
|
| 240 |
|
| 241 | elif type_name == 'builtins.float':
|
| 242 | c_type = 'double'
|
| 243 |
|
| 244 | elif type_name == 'builtins.bool':
|
| 245 | c_type = 'bool'
|
| 246 |
|
| 247 | elif type_name == 'builtins.str':
|
| 248 | if util.SMALL_STR:
|
| 249 | c_type = 'Str'
|
| 250 | is_pointer = False
|
| 251 | else:
|
| 252 | c_type = 'BigStr'
|
| 253 | is_pointer = True
|
| 254 |
|
| 255 | elif 'BigInt' in type_name:
|
| 256 | # also spelled mycpp.mylib.BigInt
|
| 257 |
|
| 258 | c_type = 'mops::BigInt'
|
| 259 | # Not a pointer!
|
| 260 |
|
| 261 | elif type_name == 'typing.IO':
|
| 262 | c_type = 'mylib::File'
|
| 263 | is_pointer = True
|
| 264 |
|
| 265 | # Parameterized types: List, Dict, Iterator
|
| 266 | elif type_name == 'builtins.list':
|
| 267 | assert len(t.args) == 1, t.args
|
| 268 | type_param = t.args[0]
|
| 269 | inner_c_type = GetCType(type_param)
|
| 270 | c_type = 'List<%s>' % inner_c_type
|
| 271 | is_pointer = True
|
| 272 |
|
| 273 | elif type_name == 'builtins.dict':
|
| 274 | params = []
|
| 275 | for type_param in t.args:
|
| 276 | params.append(GetCType(type_param))
|
| 277 | c_type = 'Dict<%s>' % ', '.join(params)
|
| 278 | is_pointer = True
|
| 279 |
|
| 280 | elif type_name == 'typing.Iterator':
|
| 281 | assert len(t.args) == 1, t.args
|
| 282 | type_param = t.args[0]
|
| 283 | inner_c_type = GetCType(type_param)
|
| 284 | c_type = 'ListIter<%s>' % inner_c_type
|
| 285 |
|
| 286 | else:
|
| 287 | parts = t.type.fullname.split('.')
|
| 288 | c_type = '%s::%s' % (parts[-2], parts[-1])
|
| 289 |
|
| 290 | # note: fullname => 'parse.Lexer'; name => 'Lexer'
|
| 291 | base_class_names = [b.type.fullname for b in t.type.bases]
|
| 292 |
|
| 293 | # Check base class for pybase.SimpleObj so we can output
|
| 294 | # expr_asdl::tok_t instead of expr_asdl::tok_t*. That is a enum, while
|
| 295 | # expr_t is a "regular base class".
|
| 296 | # NOTE: Could we avoid the typedef? If it's SimpleObj, just generate
|
| 297 | # tok_e instead?
|
| 298 |
|
| 299 | if 'asdl.pybase.SimpleObj' not in base_class_names:
|
| 300 | is_pointer = True
|
| 301 |
|
| 302 | elif isinstance(t, TupleType):
|
| 303 | inner_c_types = [GetCType(inner) for inner in t.items]
|
| 304 | c_type = 'Tuple%d<%s>' % (len(t.items), ', '.join(inner_c_types))
|
| 305 | is_pointer = True
|
| 306 |
|
| 307 | elif isinstance(t, UnionType): # Optional[T]
|
| 308 |
|
| 309 | num_items = len(t.items)
|
| 310 |
|
| 311 | if num_items == 3:
|
| 312 | # Special case for Optional[IOError_OSError] ==
|
| 313 | # Union[IOError, # OSError, None]
|
| 314 | t0 = t.items[0]
|
| 315 | t1 = t.items[1]
|
| 316 | t2 = t.items[2]
|
| 317 |
|
| 318 | assert isinstance(t0, Instance), t0
|
| 319 | assert isinstance(t1, Instance), t1
|
| 320 | t0_name = t0.type.fullname
|
| 321 | t1_name = t1.type.fullname
|
| 322 |
|
| 323 | if t0_name != 'builtins.IOError':
|
| 324 | raise NotImplementedError(
|
| 325 | 'Expected Union[IOError, OSError, None]: t0 = %s' %
|
| 326 | t0_name)
|
| 327 |
|
| 328 | if t1_name != 'builtins.OSError':
|
| 329 | raise NotImplementedError(
|
| 330 | 'Expected Union[IOError, OSError, None]: t1 = %s' %
|
| 331 | t1_name)
|
| 332 |
|
| 333 | if not isinstance(t2, NoneTyp):
|
| 334 | raise NotImplementedError(
|
| 335 | 'Expected Union[IOError, OSError, None]')
|
| 336 |
|
| 337 | c_type = 'IOError_OSError'
|
| 338 | is_pointer = True
|
| 339 |
|
| 340 | elif num_items == 2:
|
| 341 | # Optional[T]
|
| 342 |
|
| 343 | t0 = t.items[0]
|
| 344 | t1 = t.items[1]
|
| 345 |
|
| 346 | c_type = None
|
| 347 | if isinstance(t1, NoneTyp):
|
| 348 | c_type = GetCType(t.items[0])
|
| 349 | else:
|
| 350 | assert isinstance(t0, Instance), t0
|
| 351 | assert isinstance(t1, Instance), t1
|
| 352 |
|
| 353 | # Detect type alias defined in core/error.py
|
| 354 | # IOError_OSError = Union[IOError, OSError]
|
| 355 | t0_name = t0.type.fullname
|
| 356 | t1_name = t1.type.fullname
|
| 357 | if (t0_name == 'builtins.IOError' and
|
| 358 | t1_name == 'builtins.OSError'):
|
| 359 | c_type = 'IOError_OSError'
|
| 360 | is_pointer = True
|
| 361 |
|
| 362 | if c_type is None:
|
| 363 | raise NotImplementedError('Unexpected Union type %s' % t)
|
| 364 |
|
| 365 | else:
|
| 366 | raise NotImplementedError(
|
| 367 | 'Expected 2 or 3 items in Union, got %s' % num_items)
|
| 368 |
|
| 369 | else:
|
| 370 | raise NotImplementedError('MyPy type: %s %s' % (type(t), t))
|
| 371 |
|
| 372 | if is_pointer:
|
| 373 | c_type += '*'
|
| 374 |
|
| 375 | return c_type
|
| 376 |
|
| 377 |
|
| 378 | def GetCReturnType(t: Type) -> Tuple[str, bool, Optional[str]]:
|
| 379 | """
|
| 380 | Returns a C string, whether the tuple-by-value optimization was applied,
|
| 381 | and the C type of an extra output param if the function is a generator.
|
| 382 | """
|
| 383 |
|
| 384 | c_ret_type = GetCType(t)
|
| 385 |
|
| 386 | # Optimization: Return tuples BY VALUE
|
| 387 | if isinstance(t, TupleType):
|
| 388 | assert c_ret_type.endswith('*')
|
| 389 | return c_ret_type[:-1], True, None
|
| 390 | elif c_ret_type.startswith('ListIter<'):
|
| 391 | assert len(t.args) == 1, t.args
|
| 392 | inner_c_type = GetCType(t.args[0])
|
| 393 | return 'void', False, 'List<%s>*' % inner_c_type
|
| 394 | else:
|
| 395 | return c_ret_type, False, None
|
| 396 |
|
| 397 |
|
| 398 | def PythonStringLiteral(s: str) -> str:
|
| 399 | """
|
| 400 | Returns a properly quoted string.
|
| 401 | """
|
| 402 | # MyPy does bad escaping. Decode and push through json to get something
|
| 403 | # workable in C++.
|
| 404 | return json.dumps(format_strings.DecodeMyPyString(s))
|
| 405 |
|
| 406 |
|
| 407 | def _GetNoReturn(func_name: str) -> str:
|
| 408 | # Avoid C++ warnings by prepending [[noreturn]]
|
| 409 | if func_name in ('e_die', 'e_die_status', 'e_strict', 'e_usage', 'p_die'):
|
| 410 | return '[[noreturn]] '
|
| 411 | else:
|
| 412 | return ''
|
| 413 |
|
| 414 |
|
| 415 | # name, type, is_param
|
| 416 | LocalVar = Tuple[str, Type, bool]
|
| 417 |
|
| 418 | # lval_type, c_type, is_managed
|
| 419 | MemberVar = Tuple[Type, str, bool]
|
| 420 |
|
| 421 | AllMemberVars = Dict[ClassDef, Dict[str, MemberVar]]
|
| 422 |
|
| 423 | AllLocalVars = Dict[FuncDef, List[Tuple[str, Type]]]
|
| 424 |
|
| 425 |
|
| 426 | class _Shared(visitor.TypedVisitor):
|
| 427 |
|
| 428 | def __init__(
|
| 429 | self,
|
| 430 | types: Dict[Expression, Type],
|
| 431 | global_strings: 'const_pass.GlobalStrings',
|
| 432 | yield_out_params: Dict[FuncDef, Tuple[str, str]], # input
|
| 433 | # all_member_vars:
|
| 434 | # - Decl for declaring members in class { }
|
| 435 | # - Impl for rooting context managers
|
| 436 | all_member_vars: Optional[AllMemberVars] = None,
|
| 437 | ) -> None:
|
| 438 | visitor.TypedVisitor.__init__(self, types)
|
| 439 | self.global_strings = global_strings
|
| 440 | self.yield_out_params = yield_out_params
|
| 441 | self.all_member_vars = all_member_vars # for class def, and rooting
|
| 442 |
|
| 443 | # Primitives shared for default values
|
| 444 |
|
| 445 | def visit_int_expr(self, o: 'mypy.nodes.IntExpr') -> None:
|
| 446 | self.write(str(o.value))
|
| 447 |
|
| 448 | def visit_float_expr(self, o: 'mypy.nodes.FloatExpr') -> None:
|
| 449 | # e.g. for arg.t > 0.0
|
| 450 | self.write(str(o.value))
|
| 451 |
|
| 452 | def visit_str_expr(self, o: 'mypy.nodes.StrExpr') -> None:
|
| 453 | self.write(self.global_strings.GetVarName(o))
|
| 454 |
|
| 455 | def oils_visit_name_expr(self, o: 'mypy.nodes.NameExpr') -> None:
|
| 456 | if o.name == 'None':
|
| 457 | self.write('nullptr')
|
| 458 | return
|
| 459 | if o.name == 'True':
|
| 460 | self.write('true')
|
| 461 | return
|
| 462 | if o.name == 'False':
|
| 463 | self.write('false')
|
| 464 | return
|
| 465 | if o.name == 'self':
|
| 466 | self.write('this')
|
| 467 | return
|
| 468 |
|
| 469 | self.write(o.name)
|
| 470 |
|
| 471 | def visit_unary_expr(self, o: 'mypy.nodes.UnaryExpr') -> None:
|
| 472 | # e.g. a[-1] or 'not x'
|
| 473 | if o.op == 'not':
|
| 474 | op_str = '!'
|
| 475 | else:
|
| 476 | op_str = o.op
|
| 477 | self.write(op_str)
|
| 478 | self.accept(o.expr)
|
| 479 |
|
| 480 | def _NamespaceComment(self) -> str:
|
| 481 | # abstract method
|
| 482 | raise NotImplementedError()
|
| 483 |
|
| 484 | def oils_visit_mypy_file(self, o: 'mypy.nodes.MypyFile') -> None:
|
| 485 | mod_parts = o.fullname.split('.')
|
| 486 | comment = self._NamespaceComment()
|
| 487 |
|
| 488 | self.write_ind('namespace %s { // %s\n', mod_parts[-1], comment)
|
| 489 | self.write('\n')
|
| 490 |
|
| 491 | #self.log('defs %s', o.defs)
|
| 492 | for node in o.defs:
|
| 493 | self.accept(node)
|
| 494 |
|
| 495 | self.write('\n')
|
| 496 | self.write_ind('} // %s namespace %s\n', comment, mod_parts[-1])
|
| 497 | self.write('\n')
|
| 498 |
|
| 499 | def _WriteFuncParams(self,
|
| 500 | func_def: FuncDef,
|
| 501 | write_defaults: bool = False) -> None:
|
| 502 | """Write params for function/method signatures."""
|
| 503 | arg_types = func_def.type.arg_types
|
| 504 | arguments = func_def.arguments
|
| 505 |
|
| 506 | is_first = True # EXCLUDING 'self'
|
| 507 | for arg_type, arg in zip(arg_types, arguments):
|
| 508 | if not is_first:
|
| 509 | self.write(', ')
|
| 510 |
|
| 511 | c_type = GetCType(arg_type)
|
| 512 |
|
| 513 | arg_name = arg.variable.name
|
| 514 |
|
| 515 | # C++ has implicit 'this'
|
| 516 | if arg_name == 'self':
|
| 517 | continue
|
| 518 |
|
| 519 | # int foo
|
| 520 | self.write('%s %s', c_type, arg_name)
|
| 521 |
|
| 522 | if write_defaults and arg.initializer: # int foo = 42
|
| 523 | self.write(' = ')
|
| 524 | self.accept(arg.initializer)
|
| 525 |
|
| 526 | is_first = False
|
| 527 |
|
| 528 | if 0:
|
| 529 | self.log('Argument %s', arg.variable)
|
| 530 | self.log(' type_annotation %s', arg.type_annotation)
|
| 531 | # I think these are for default values
|
| 532 | self.log(' initializer %s', arg.initializer)
|
| 533 | self.log(' kind %s', arg.kind)
|
| 534 |
|
| 535 | # Is the function we're writing params for an iterator?
|
| 536 | if func_def in self.yield_out_params:
|
| 537 | self.write(', ')
|
| 538 |
|
| 539 | arg_name, c_type = self.yield_out_params[func_def]
|
| 540 | self.write('%s %s', c_type, arg_name)
|
| 541 |
|
| 542 |
|
| 543 | class Decl(_Shared):
|
| 544 |
|
| 545 | def __init__(
|
| 546 | self,
|
| 547 | types: Dict[Expression, Type],
|
| 548 | global_strings: 'const_pass.GlobalStrings',
|
| 549 | yield_out_params: Dict[FuncDef, Tuple[str, str]], # input
|
| 550 | virtual: pass_state.Virtual = None,
|
| 551 | all_member_vars: Optional[AllMemberVars] = None,
|
| 552 | ) -> None:
|
| 553 | _Shared.__init__(
|
| 554 | self,
|
| 555 | types,
|
| 556 | global_strings,
|
| 557 | yield_out_params,
|
| 558 | all_member_vars=all_member_vars,
|
| 559 | )
|
| 560 | self.virtual = virtual
|
| 561 |
|
| 562 | def _NamespaceComment(self) -> str:
|
| 563 | # abstract method
|
| 564 | return 'declare'
|
| 565 |
|
| 566 | def oils_visit_func_def(self, o: 'mypy.nodes.FuncDef') -> None:
|
| 567 | # Avoid C++ warnings by prepending [[noreturn]]
|
| 568 | noreturn = _GetNoReturn(o.name)
|
| 569 |
|
| 570 | virtual = ''
|
| 571 | if self.virtual.IsVirtual(self.current_class_name, o.name):
|
| 572 | virtual = 'virtual '
|
| 573 |
|
| 574 | # declaration inside class { }
|
| 575 | func_name = o.name
|
| 576 |
|
| 577 | # Why can't we get this Type object with self.types[o]?
|
| 578 | c_ret_type, _, _ = GetCReturnType(o.type.ret_type)
|
| 579 |
|
| 580 | self.write_ind('%s%s%s %s(', noreturn, virtual, c_ret_type, func_name)
|
| 581 |
|
| 582 | self._WriteFuncParams(o, write_defaults=True)
|
| 583 | self.write(');\n')
|
| 584 |
|
| 585 | def oils_visit_member_expr(self, o: 'mypy.nodes.MemberExpr') -> None:
|
| 586 | # In declarations, 'a.b' is only used for default argument
|
| 587 | # values 'a::b'
|
| 588 | self.accept(o.expr)
|
| 589 | # TODO: remove write() in Decl pass
|
| 590 | self.write('::')
|
| 591 | self.write(o.name)
|
| 592 |
|
| 593 | def oils_visit_assignment_stmt(self, o: 'mypy.nodes.AssignmentStmt',
|
| 594 | lval: Expression, rval: Expression) -> None:
|
| 595 | # Declare constant strings. They have to be at the top level.
|
| 596 |
|
| 597 | # TODO: self.at_global_scope doesn't work for context managers and so forth
|
| 598 | if self.indent == 0:
|
| 599 | # Top level can't have foo.bar = baz
|
| 600 | assert isinstance(lval, NameExpr), lval
|
| 601 | if not util.SkipAssignment(lval.name):
|
| 602 | c_type = GetCType(self.types[lval])
|
| 603 | self.write('extern %s %s;\n', c_type, lval.name)
|
| 604 |
|
| 605 | # TODO: we don't traverse here, so _CheckCondition() isn't called
|
| 606 | # e.g. x = 'a' if mylist else 'b'
|
| 607 |
|
| 608 | def oils_visit_constructor(self, o: ClassDef, stmt: FuncDef,
|
| 609 | base_class_sym: util.SymbolPath) -> None:
|
| 610 | self.indent += 1
|
| 611 | self.write_ind('%s(', o.name)
|
| 612 | self._WriteFuncParams(stmt, write_defaults=True)
|
| 613 | self.write(');\n')
|
| 614 | self.indent -= 1
|
| 615 |
|
| 616 | def oils_visit_dunder_exit(self, o: ClassDef, stmt: FuncDef,
|
| 617 | base_class_sym: util.SymbolPath) -> None:
|
| 618 | self.indent += 1
|
| 619 | # Turn it into a destructor with NO ARGS
|
| 620 | self.write_ind('~%s();\n', o.name)
|
| 621 | self.indent -= 1
|
| 622 |
|
| 623 | def oils_visit_method(self, o: ClassDef, stmt: FuncDef,
|
| 624 | base_class_sym: util.SymbolPath) -> None:
|
| 625 | self.indent += 1
|
| 626 | self.accept(stmt)
|
| 627 | self.indent -= 1
|
| 628 |
|
| 629 | def oils_visit_class_members(self, o: ClassDef,
|
| 630 | base_class_sym: util.SymbolPath) -> None:
|
| 631 | # Write member variables
|
| 632 | self.indent += 1
|
| 633 | self._MemberDecl(o, base_class_sym)
|
| 634 | self.indent -= 1
|
| 635 |
|
| 636 | def oils_visit_class_def(
|
| 637 | self, o: 'mypy.nodes.ClassDef',
|
| 638 | base_class_sym: Optional[util.SymbolPath]) -> None:
|
| 639 | self.write_ind('class %s', o.name) # block after this
|
| 640 |
|
| 641 | # e.g. class TextOutput : public ColorOutput
|
| 642 | if base_class_sym:
|
| 643 | self.write(' : public %s',
|
| 644 | SymbolToString(base_class_sym, strip_package=True))
|
| 645 |
|
| 646 | self.write(' {\n')
|
| 647 | self.write_ind(' public:\n')
|
| 648 |
|
| 649 | # This visits all the methods, with self.indent += 1, param
|
| 650 | # base_class_sym, self.current_method_name
|
| 651 |
|
| 652 | super().oils_visit_class_def(o, base_class_sym)
|
| 653 |
|
| 654 | self.write_ind('};\n')
|
| 655 | self.write('\n')
|
| 656 |
|
| 657 | def _GcHeaderDecl(self, o: 'mypy.nodes.ClassDef',
|
| 658 | field_gc: Tuple[str, str], mask_bits: List[str]) -> None:
|
| 659 | if mask_bits:
|
| 660 | self.write_ind('\n')
|
| 661 | self.write_ind('static constexpr uint32_t field_mask() {\n')
|
| 662 | self.write_ind(' return ')
|
| 663 | for i, b in enumerate(mask_bits):
|
| 664 | if i != 0:
|
| 665 | self.write('\n')
|
| 666 | self.write_ind(' | ')
|
| 667 | self.write(b)
|
| 668 | self.write(';\n')
|
| 669 | self.write_ind('}\n')
|
| 670 |
|
| 671 | obj_tag, obj_arg = field_gc
|
| 672 | if obj_tag == 'HeapTag::FixedSize':
|
| 673 | obj_mask = obj_arg
|
| 674 | obj_header = 'ObjHeader::ClassFixed(%s, sizeof(%s))' % (obj_mask,
|
| 675 | o.name)
|
| 676 | elif obj_tag == 'HeapTag::Scanned':
|
| 677 | num_pointers = obj_arg
|
| 678 | obj_header = 'ObjHeader::ClassScanned(%s, sizeof(%s))' % (
|
| 679 | num_pointers, o.name)
|
| 680 | else:
|
| 681 | raise AssertionError(o.name)
|
| 682 |
|
| 683 | self.write('\n')
|
| 684 | self.write_ind('static constexpr ObjHeader obj_header() {\n')
|
| 685 | self.write_ind(' return %s;\n' % obj_header)
|
| 686 | self.write_ind('}\n')
|
| 687 |
|
| 688 | def _MemberDecl(self, o: 'mypy.nodes.ClassDef',
|
| 689 | base_class_sym: util.SymbolPath) -> None:
|
| 690 | member_vars = self.all_member_vars[o]
|
| 691 |
|
| 692 | # List of field mask expressions
|
| 693 | mask_bits = []
|
| 694 | if self.virtual.CanReorderFields(SplitPyName(o.fullname)):
|
| 695 | # No inheritance, so we are free to REORDER member vars, putting
|
| 696 | # pointers at the front.
|
| 697 |
|
| 698 | pointer_members = []
|
| 699 | non_pointer_members = []
|
| 700 |
|
| 701 | for name in member_vars:
|
| 702 | _, c_type, is_managed = member_vars[name]
|
| 703 | if is_managed:
|
| 704 | pointer_members.append(name)
|
| 705 | else:
|
| 706 | non_pointer_members.append(name)
|
| 707 |
|
| 708 | # So we declare them in the right order
|
| 709 | sorted_member_names = pointer_members + non_pointer_members
|
| 710 |
|
| 711 | field_gc = ('HeapTag::Scanned', str(len(pointer_members)))
|
| 712 | else:
|
| 713 | # Has inheritance
|
| 714 |
|
| 715 | # The field mask of a derived class is unioned with its base's
|
| 716 | # field mask.
|
| 717 | if base_class_sym:
|
| 718 | mask_bits.append(
|
| 719 | '%s::field_mask()' %
|
| 720 | SymbolToString(base_class_sym, strip_package=True))
|
| 721 |
|
| 722 | for name in sorted(member_vars):
|
| 723 | _, c_type, is_managed = member_vars[name]
|
| 724 | if is_managed:
|
| 725 | mask_bits.append('maskbit(offsetof(%s, %s))' %
|
| 726 | (o.name, name))
|
| 727 |
|
| 728 | # A base class with no fields has kZeroMask.
|
| 729 | if not base_class_sym and not mask_bits:
|
| 730 | mask_bits.append('kZeroMask')
|
| 731 |
|
| 732 | sorted_member_names = sorted(member_vars)
|
| 733 |
|
| 734 | field_gc = ('HeapTag::FixedSize', 'field_mask()')
|
| 735 |
|
| 736 | # Write member variables
|
| 737 |
|
| 738 | #log('MEMBERS for %s: %s', o.name, list(self.member_vars.keys()))
|
| 739 | if len(member_vars):
|
| 740 | if base_class_sym:
|
| 741 | self.write('\n') # separate from functions
|
| 742 |
|
| 743 | for name in sorted_member_names:
|
| 744 | _, c_type, _ = member_vars[name]
|
| 745 | # use default zero initialization for all members
|
| 746 | # (context managers may be on the stack)
|
| 747 | self.write_ind('%s %s{};\n', c_type, name)
|
| 748 |
|
| 749 | # Context managers aren't GC objects
|
| 750 | if not _IsContextManager(self.current_class_name):
|
| 751 | self._GcHeaderDecl(o, field_gc, mask_bits)
|
| 752 |
|
| 753 | self.write('\n')
|
| 754 | self.write_ind('DISALLOW_COPY_AND_ASSIGN(%s)\n', o.name)
|
| 755 |
|
| 756 |
|
| 757 | class Impl(_Shared):
|
| 758 |
|
| 759 | def __init__(
|
| 760 | self,
|
| 761 | types: Dict[Expression, Type],
|
| 762 | global_strings: 'const_pass.GlobalStrings',
|
| 763 | yield_out_params: Dict[FuncDef, Tuple[str, str]], # input
|
| 764 | all_member_vars: Optional[AllMemberVars] = None,
|
| 765 | local_vars: Optional[AllLocalVars] = None,
|
| 766 | dot_exprs: Optional['conversion_pass.DotExprs'] = None,
|
| 767 | stack_roots_warn: Optional[int] = None,
|
| 768 | stack_roots: Optional[pass_state.StackRoots] = None) -> None:
|
| 769 | _Shared.__init__(self,
|
| 770 | types,
|
| 771 | global_strings,
|
| 772 | yield_out_params,
|
| 773 | all_member_vars=all_member_vars)
|
| 774 | self.local_vars = local_vars
|
| 775 |
|
| 776 | # Computed in previous passes
|
| 777 | self.dot_exprs = dot_exprs
|
| 778 | self.stack_roots_warn = stack_roots_warn
|
| 779 | self.stack_roots = stack_roots
|
| 780 |
|
| 781 | # Traversal state used to to create an EAGER List<T>
|
| 782 | self.yield_eager_assign: Dict[AssignmentStmt, Tuple[str, str]] = {}
|
| 783 | self.yield_eager_for: Dict[ForStmt, Tuple[str, str]] = {}
|
| 784 |
|
| 785 | self.yield_assign_node: Optional[AssignmentStmt] = None
|
| 786 | self.yield_for_node: Optional[ForStmt] = None
|
| 787 |
|
| 788 | # More Traversal state
|
| 789 | self.current_func_node: Optional[FuncDef] = None
|
| 790 |
|
| 791 | self.unique_id = 0
|
| 792 |
|
| 793 | def _NamespaceComment(self) -> str:
|
| 794 | # abstract method
|
| 795 | return 'define'
|
| 796 |
|
| 797 | def oils_visit_func_def(self, o: 'mypy.nodes.FuncDef') -> None:
|
| 798 | if self.current_class_name:
|
| 799 | # definition looks like
|
| 800 | # void Class::method(...);
|
| 801 | func_name = SymbolToString((self.current_class_name[-1], o.name))
|
| 802 | noreturn = ''
|
| 803 | else:
|
| 804 | func_name = o.name
|
| 805 | noreturn = _GetNoReturn(o.name)
|
| 806 |
|
| 807 | self.write('\n')
|
| 808 |
|
| 809 | # Why can't we get this Type object with self.types[o]?
|
| 810 | c_ret_type, _, _ = GetCReturnType(o.type.ret_type)
|
| 811 |
|
| 812 | self.write_ind('%s%s %s(', noreturn, c_ret_type, func_name)
|
| 813 |
|
| 814 | self.current_func_node = o
|
| 815 | self._WriteFuncParams(o, write_defaults=False)
|
| 816 |
|
| 817 | self.write(') ')
|
| 818 | arg_names = [arg.variable.name for arg in o.arguments]
|
| 819 | #log('arg_names %s', arg_names)
|
| 820 | #log('local_vars %s', self.local_vars[o])
|
| 821 | local_var_list: List[LocalVar] = []
|
| 822 | for (lval_name, lval_type) in self.local_vars[o]:
|
| 823 | local_var_list.append((lval_name, lval_type, lval_name
|
| 824 | in arg_names))
|
| 825 |
|
| 826 | self.write('{\n')
|
| 827 |
|
| 828 | self.indent += 1
|
| 829 | self._WriteLocals(local_var_list)
|
| 830 | self._WriteBody(o.body.body)
|
| 831 | self.indent -= 1
|
| 832 |
|
| 833 | self.write('}\n')
|
| 834 |
|
| 835 | self.current_func_node = None
|
| 836 |
|
| 837 | def visit_yield_expr(self, o: 'mypy.nodes.YieldExpr') -> None:
|
| 838 | assert self.current_func_node in self.yield_out_params
|
| 839 | self.write('%s->append(',
|
| 840 | self.yield_out_params[self.current_func_node][0])
|
| 841 | self.accept(o.expr)
|
| 842 | self.write(')')
|
| 843 |
|
| 844 | def _WriteArgList(self, args: List[Expression]) -> None:
|
| 845 | self.write('(')
|
| 846 | for i, arg in enumerate(args):
|
| 847 | if i != 0:
|
| 848 | self.write(', ')
|
| 849 | self.accept(arg)
|
| 850 |
|
| 851 | # Pass an extra arg like my_generator(42, &accum)
|
| 852 | #
|
| 853 | # Two cases:
|
| 854 | # ForStmt: for y in generator(42): =>
|
| 855 | # generator(42, &y)
|
| 856 | # AssignmentStmt: it = generator(42) =>
|
| 857 | # List<int> _iter_buf_it;
|
| 858 | # generator(42, &iter_buf_it); # eagerly append
|
| 859 |
|
| 860 | eager_pair = (self.yield_eager_assign.get(self.yield_assign_node) or
|
| 861 | self.yield_eager_for.get(self.yield_for_node))
|
| 862 |
|
| 863 | if eager_pair:
|
| 864 | if len(args) > 0:
|
| 865 | self.write(', ')
|
| 866 |
|
| 867 | eager_list_name, _ = eager_pair
|
| 868 | self.write('&%s', eager_list_name)
|
| 869 |
|
| 870 | self.write(')')
|
| 871 |
|
| 872 | def oils_visit_member_expr(self, o: 'mypy.nodes.MemberExpr') -> None:
|
| 873 | dot_expr = self.dot_exprs[o]
|
| 874 |
|
| 875 | if isinstance(dot_expr, pass_state.StackObjectMember):
|
| 876 | op = '.'
|
| 877 |
|
| 878 | elif (isinstance(dot_expr, pass_state.StaticClassMember) or
|
| 879 | isinstance(dot_expr, pass_state.ModuleMember)):
|
| 880 | op = '::'
|
| 881 |
|
| 882 | elif isinstance(dot_expr, pass_state.HeapObjectMember):
|
| 883 | op = '->'
|
| 884 |
|
| 885 | else:
|
| 886 | raise AssertionError()
|
| 887 |
|
| 888 | self.accept(o.expr)
|
| 889 | self.write(op)
|
| 890 |
|
| 891 | if o.name == 'errno':
|
| 892 | # e->errno -> e->errno_ to avoid conflict with C macro
|
| 893 | self.write('errno_')
|
| 894 | else:
|
| 895 | self.write('%s', o.name)
|
| 896 |
|
| 897 | def _IsInstantiation(self, o: 'mypy.nodes.CallExpr') -> bool:
|
| 898 | callee_name = o.callee.name
|
| 899 | callee_type = self.types[o.callee]
|
| 900 |
|
| 901 | # e.g. int() takes str, float, etc. It doesn't matter for translation.
|
| 902 | if isinstance(callee_type, Overloaded):
|
| 903 | if 0:
|
| 904 | for item in callee_type.items():
|
| 905 | self.log('item: %s', item)
|
| 906 |
|
| 907 | if isinstance(callee_type, CallableType):
|
| 908 | # If the function name is the same as the return type, then add
|
| 909 | # 'Alloc<>'. f = Foo() => f = Alloc<Foo>().
|
| 910 | ret_type = callee_type.ret_type
|
| 911 |
|
| 912 | # e.g. str(i) is a free function
|
| 913 | if (callee_name not in ('str', 'bool', 'float') and
|
| 914 | 'BigInt' not in callee_name and
|
| 915 | isinstance(ret_type, Instance)):
|
| 916 |
|
| 917 | ret_type_name = ret_type.type.name
|
| 918 |
|
| 919 | # HACK: Const is the callee; expr__Const is the return type
|
| 920 | if (ret_type_name == callee_name or
|
| 921 | ret_type_name.endswith('__' + callee_name)):
|
| 922 | return True
|
| 923 |
|
| 924 | return False
|
| 925 |
|
| 926 | def oils_visit_probe_call(self, o: 'mypy.nodes.CallExpr') -> None:
|
| 927 | assert len(o.args) >= 2 and len(o.args) < 13, o.args
|
| 928 | assert isinstance(o.args[0], mypy.nodes.StrExpr), o.args[0]
|
| 929 | assert isinstance(o.args[1], mypy.nodes.StrExpr), o.args[1]
|
| 930 | arity = len(o.args) - 2
|
| 931 | macro = 'DTRACE_PROBE'
|
| 932 | if arity > 0:
|
| 933 | macro = 'DTRACE_PROBE%d' % arity
|
| 934 |
|
| 935 | self.write('%s(%s, %s', macro, o.args[0].value, o.args[1].value)
|
| 936 |
|
| 937 | for arg in o.args[2:]:
|
| 938 | arg_type = self.types[arg]
|
| 939 | self.write(', ')
|
| 940 | if util.IsStr(arg_type): # TODO: doesn't know it's an Instance
|
| 941 | self.write('%s->data()' % arg.name)
|
| 942 | else:
|
| 943 | self.accept(arg)
|
| 944 |
|
| 945 | self.write(')')
|
| 946 |
|
| 947 | def oils_visit_log_call(self, fmt: StrExpr,
|
| 948 | args: List[Expression]) -> None:
|
| 949 | if len(args) == 0: # log('const') -> print_stderr(S_xyz)
|
| 950 | # This is a GC string
|
| 951 | self.write('mylib::print_stderr(')
|
| 952 | self.accept(fmt)
|
| 953 | self.write(')')
|
| 954 | return
|
| 955 |
|
| 956 | # log('const %s', a) -> print_stderr(StrFormat("const %s", a))
|
| 957 | quoted_fmt = PythonStringLiteral(fmt.value)
|
| 958 | self.write('mylib::print_stderr(StrFormat(%s, ' % quoted_fmt)
|
| 959 |
|
| 960 | for i, arg in enumerate(args):
|
| 961 | if i != 0:
|
| 962 | self.write(', ')
|
| 963 | self.accept(arg)
|
| 964 | self.write('))')
|
| 965 |
|
| 966 | def oils_visit_call_expr(self, o: 'mypy.nodes.CallExpr') -> None:
|
| 967 | callee_name = o.callee.name
|
| 968 |
|
| 969 | # return cast(ShArrayLiteral, tok)
|
| 970 | # -> return static_cast<ShArrayLiteral*>(tok)
|
| 971 |
|
| 972 | # TODO: Consolidate this with AssignmentExpr logic.
|
| 973 | if callee_name == 'cast':
|
| 974 | call = o
|
| 975 | type_expr = call.args[0]
|
| 976 |
|
| 977 | subtype_name = _GetCTypeForCast(type_expr)
|
| 978 | cast_kind = _GetCastKind(self.module_path, subtype_name)
|
| 979 | self.write('%s<%s>(', cast_kind, subtype_name)
|
| 980 | self.accept(call.args[1]) # variable being casted
|
| 981 | self.write(')')
|
| 982 | return
|
| 983 |
|
| 984 | if isinstance(o.callee, MemberExpr) and callee_name == 'next':
|
| 985 | self.accept(o.callee.expr)
|
| 986 | self.write('.iterNext')
|
| 987 | self._WriteArgList(o.args)
|
| 988 | return
|
| 989 |
|
| 990 | if self._IsInstantiation(o):
|
| 991 | self.write('Alloc<')
|
| 992 | self.accept(o.callee)
|
| 993 | self.write('>')
|
| 994 | self._WriteArgList(o.args)
|
| 995 | return
|
| 996 |
|
| 997 | # Namespace.
|
| 998 | if callee_name == 'int': # int('foo') in Python conflicts with keyword
|
| 999 | self.write('to_int')
|
| 1000 | elif callee_name == 'float':
|
| 1001 | self.write('to_float')
|
| 1002 | elif callee_name == 'bool':
|
| 1003 | self.write('to_bool')
|
| 1004 | else:
|
| 1005 | self.accept(o.callee) # could be f() or obj.method()
|
| 1006 |
|
| 1007 | self._WriteArgList(o.args)
|
| 1008 |
|
| 1009 | # TODO: we could check that keyword arguments are passed as named args?
|
| 1010 | #self.log(' arg_kinds %s', o.arg_kinds)
|
| 1011 | #self.log(' arg_names %s', o.arg_names)
|
| 1012 |
|
| 1013 | def oils_visit_format_expr(self, left: Expression,
|
| 1014 | right: Expression) -> None:
|
| 1015 | self.write('StrFormat(')
|
| 1016 | if isinstance(left, StrExpr):
|
| 1017 | self.write(PythonStringLiteral(left.value))
|
| 1018 | else:
|
| 1019 | self.accept(left)
|
| 1020 | #log('right_type %s', right_type)
|
| 1021 |
|
| 1022 | right_type = self.types[right]
|
| 1023 |
|
| 1024 | # TODO: Can we restore some type checking?
|
| 1025 | if 0:
|
| 1026 | if isinstance(right_type, Instance):
|
| 1027 | fmt_types: List[Type] = [right_type]
|
| 1028 | elif isinstance(right_type, TupleType):
|
| 1029 | fmt_types = right_type.items
|
| 1030 | # Handle Optional[str]
|
| 1031 | elif (isinstance(right_type, UnionType) and
|
| 1032 | len(right_type.items) == 2 and
|
| 1033 | isinstance(right_type.items[1], NoneTyp)):
|
| 1034 | fmt_types = [right_type.items[0]]
|
| 1035 | else:
|
| 1036 | raise AssertionError(right_type)
|
| 1037 |
|
| 1038 | # In the definition pass, write the call site.
|
| 1039 | if isinstance(right_type, TupleType):
|
| 1040 | assert isinstance(right, TupleExpr), right
|
| 1041 | for i, item in enumerate(right.items):
|
| 1042 | self.write(', ')
|
| 1043 | self.accept(item)
|
| 1044 |
|
| 1045 | else: # '[%s]' % x
|
| 1046 | self.write(', ')
|
| 1047 | self.accept(right)
|
| 1048 |
|
| 1049 | self.write(')')
|
| 1050 |
|
| 1051 | def oils_visit_op_expr(self, o: 'mypy.nodes.OpExpr') -> None:
|
| 1052 | # a + b when a and b are strings. (Can't use operator overloading
|
| 1053 | # because they're pointers.)
|
| 1054 | left_type = self.types[o.left]
|
| 1055 | right_type = self.types[o.right]
|
| 1056 |
|
| 1057 | # NOTE: Need GetCType to handle Optional[BigStr*] in ASDL schemas.
|
| 1058 | # Could tighten it up later.
|
| 1059 | left_ctype = GetCType(left_type)
|
| 1060 | right_ctype = GetCType(right_type)
|
| 1061 |
|
| 1062 | c_op = o.op
|
| 1063 | if left_ctype == right_ctype == 'int' and c_op == '//':
|
| 1064 | # integer division // -> /
|
| 1065 | c_op = '/'
|
| 1066 |
|
| 1067 | # 'abc' + 'def'
|
| 1068 | if left_ctype == right_ctype == 'BigStr*' and c_op == '+':
|
| 1069 | self.write('str_concat(')
|
| 1070 | self.accept(o.left)
|
| 1071 | self.write(', ')
|
| 1072 | self.accept(o.right)
|
| 1073 | self.write(')')
|
| 1074 | return
|
| 1075 |
|
| 1076 | # 'abc' * 3
|
| 1077 | if left_ctype == 'BigStr*' and right_ctype == 'int' and c_op == '*':
|
| 1078 | self.write('str_repeat(')
|
| 1079 | self.accept(o.left)
|
| 1080 | self.write(', ')
|
| 1081 | self.accept(o.right)
|
| 1082 | self.write(')')
|
| 1083 | return
|
| 1084 |
|
| 1085 | # [None] * 3 => list_repeat(None, 3)
|
| 1086 | if (left_ctype.startswith('List<') and right_ctype == 'int' and
|
| 1087 | c_op == '*'):
|
| 1088 | self.write('list_repeat(')
|
| 1089 | self.accept(o.left.items[0])
|
| 1090 | self.write(', ')
|
| 1091 | self.accept(o.right)
|
| 1092 | self.write(')')
|
| 1093 | return
|
| 1094 |
|
| 1095 | # These parens are sometimes extra, but sometimes required. Example:
|
| 1096 | #
|
| 1097 | # if ((a and (false or true))) { # right
|
| 1098 | # vs.
|
| 1099 | # if (a and false or true)) { # wrong
|
| 1100 | self.write('(')
|
| 1101 | self.accept(o.left)
|
| 1102 | self.write(' %s ', c_op)
|
| 1103 | self.accept(o.right)
|
| 1104 | self.write(')')
|
| 1105 |
|
| 1106 | def visit_comparison_expr(self, o: 'mypy.nodes.ComparisonExpr') -> None:
|
| 1107 | # Make sure it's binary
|
| 1108 | assert len(o.operators) == 1, o.operators
|
| 1109 | assert len(o.operands) == 2, o.operands
|
| 1110 |
|
| 1111 | operator = o.operators[0]
|
| 1112 | left = o.operands[0]
|
| 1113 | right = o.operands[1]
|
| 1114 |
|
| 1115 | # Assume is and is not are for None / nullptr comparison.
|
| 1116 | if operator == 'is': # foo is None => foo == nullptr
|
| 1117 | self.accept(o.operands[0])
|
| 1118 | self.write(' == ')
|
| 1119 | self.accept(o.operands[1])
|
| 1120 | return
|
| 1121 |
|
| 1122 | if operator == 'is not': # foo is not None => foo != nullptr
|
| 1123 | self.accept(o.operands[0])
|
| 1124 | self.write(' != ')
|
| 1125 | self.accept(o.operands[1])
|
| 1126 | return
|
| 1127 |
|
| 1128 | t0 = self.types[left]
|
| 1129 | t1 = self.types[right]
|
| 1130 |
|
| 1131 | # 0: not a special case
|
| 1132 | # 1: str
|
| 1133 | # 2: Optional[str] which is Union[str, None]
|
| 1134 | left_type_i = 0 # not a special case
|
| 1135 | right_type_i = 0 # not a special case
|
| 1136 |
|
| 1137 | if util.IsStr(t0):
|
| 1138 | left_type_i = 1
|
| 1139 | elif (isinstance(t0, UnionType) and len(t0.items) == 2 and
|
| 1140 | util.IsStr(t0.items[0]) and isinstance(t0.items[1], NoneTyp)):
|
| 1141 | left_type_i = 2
|
| 1142 |
|
| 1143 | if util.IsStr(t1):
|
| 1144 | right_type_i = 1
|
| 1145 | elif (isinstance(t1, UnionType) and len(t1.items) == 2 and
|
| 1146 | util.IsStr(t1.items[0]) and isinstance(t1.items[1], NoneTyp)):
|
| 1147 | right_type_i = 2
|
| 1148 |
|
| 1149 | #self.log('left_type_i %s right_type_i %s', left_type, right_type)
|
| 1150 |
|
| 1151 | if left_type_i > 0 and right_type_i > 0 and operator in ('==', '!='):
|
| 1152 | if operator == '!=':
|
| 1153 | self.write('!(')
|
| 1154 |
|
| 1155 | # NOTE: This could also be str_equals(left, right)? Does it make a
|
| 1156 | # difference?
|
| 1157 | if left_type_i > 1 or right_type_i > 1:
|
| 1158 | self.write('maybe_str_equals(')
|
| 1159 | else:
|
| 1160 | self.write('str_equals(')
|
| 1161 | self.accept(left)
|
| 1162 | self.write(', ')
|
| 1163 | self.accept(right)
|
| 1164 | self.write(')')
|
| 1165 |
|
| 1166 | if operator == '!=':
|
| 1167 | self.write(')')
|
| 1168 | return
|
| 1169 |
|
| 1170 | # Note: we could get rid of this altogether and rely on C++ function
|
| 1171 | # overloading. But somehow I like it more explicit, closer to C (even
|
| 1172 | # though we use templates).
|
| 1173 | contains_func = _ContainsFunc(t1)
|
| 1174 |
|
| 1175 | if operator == 'in':
|
| 1176 | if isinstance(right, TupleExpr):
|
| 1177 | left_type = self.types[left]
|
| 1178 |
|
| 1179 | equals_func = _EqualsFunc(left_type)
|
| 1180 |
|
| 1181 | # x in (1, 2, 3) => (x == 1 || x == 2 || x == 3)
|
| 1182 | self.write('(')
|
| 1183 |
|
| 1184 | for i, item in enumerate(right.items):
|
| 1185 | if i != 0:
|
| 1186 | self.write(' || ')
|
| 1187 |
|
| 1188 | if equals_func:
|
| 1189 | self.write('%s(' % equals_func)
|
| 1190 | self.accept(left)
|
| 1191 | self.write(', ')
|
| 1192 | self.accept(item)
|
| 1193 | self.write(')')
|
| 1194 | else:
|
| 1195 | self.accept(left)
|
| 1196 | self.write(' == ')
|
| 1197 | self.accept(item)
|
| 1198 |
|
| 1199 | self.write(')')
|
| 1200 | return
|
| 1201 |
|
| 1202 | assert contains_func, "RHS of 'in' has type %r" % t1
|
| 1203 | # x in mylist => list_contains(mylist, x)
|
| 1204 | self.write('%s(', contains_func)
|
| 1205 | self.accept(right)
|
| 1206 | self.write(', ')
|
| 1207 | self.accept(left)
|
| 1208 | self.write(')')
|
| 1209 | return
|
| 1210 |
|
| 1211 | if operator == 'not in':
|
| 1212 | if isinstance(right, TupleExpr):
|
| 1213 | left_type = self.types[left]
|
| 1214 | equals_func = _EqualsFunc(left_type)
|
| 1215 |
|
| 1216 | # x not in (1, 2, 3) => (x != 1 && x != 2 && x != 3)
|
| 1217 | self.write('(')
|
| 1218 |
|
| 1219 | for i, item in enumerate(right.items):
|
| 1220 | if i != 0:
|
| 1221 | self.write(' && ')
|
| 1222 |
|
| 1223 | if equals_func:
|
| 1224 | self.write('!%s(' % equals_func)
|
| 1225 | self.accept(left)
|
| 1226 | self.write(', ')
|
| 1227 | self.accept(item)
|
| 1228 | self.write(')')
|
| 1229 | else:
|
| 1230 | self.accept(left)
|
| 1231 | self.write(' != ')
|
| 1232 | self.accept(item)
|
| 1233 |
|
| 1234 | self.write(')')
|
| 1235 | return
|
| 1236 |
|
| 1237 | assert contains_func, t1
|
| 1238 |
|
| 1239 | # x not in mylist => !list_contains(mylist, x)
|
| 1240 | self.write('!%s(', contains_func)
|
| 1241 | self.accept(right)
|
| 1242 | self.write(', ')
|
| 1243 | self.accept(left)
|
| 1244 | self.write(')')
|
| 1245 | return
|
| 1246 |
|
| 1247 | # Default case
|
| 1248 | self.accept(o.operands[0])
|
| 1249 | self.write(' %s ', o.operators[0])
|
| 1250 | self.accept(o.operands[1])
|
| 1251 |
|
| 1252 | def _WriteListElements(self,
|
| 1253 | items: List[Expression],
|
| 1254 | sep: str = ', ') -> None:
|
| 1255 | # sep may be 'COMMA' for a macro
|
| 1256 | self.write('{')
|
| 1257 | for i, item in enumerate(items):
|
| 1258 | if i != 0:
|
| 1259 | self.write(sep)
|
| 1260 | self.accept(item)
|
| 1261 | self.write('}')
|
| 1262 |
|
| 1263 | def visit_list_expr(self, o: 'mypy.nodes.ListExpr') -> None:
|
| 1264 | list_type = self.types[o]
|
| 1265 | # Note: need a lookup function that understands ListExpr -> Instance
|
| 1266 | assert isinstance(list_type, Instance), list_type
|
| 1267 |
|
| 1268 | #self.log('**** list_type = %s', list_type)
|
| 1269 | c_type = GetCType(list_type)
|
| 1270 |
|
| 1271 | item_type = list_type.args[0] # int for List[int]
|
| 1272 | item_c_type = GetCType(item_type)
|
| 1273 |
|
| 1274 | assert c_type.endswith('*'), c_type
|
| 1275 | c_type = c_type[:-1] # HACK TO CLEAN UP
|
| 1276 |
|
| 1277 | if len(o.items) == 0:
|
| 1278 | self.write('Alloc<%s>()' % c_type)
|
| 1279 | else:
|
| 1280 | self.write('NewList<%s>(std::initializer_list<%s>' %
|
| 1281 | (item_c_type, item_c_type))
|
| 1282 | self._WriteListElements(o.items)
|
| 1283 | self.write(')')
|
| 1284 |
|
| 1285 | def visit_dict_expr(self, o: 'mypy.nodes.DictExpr') -> None:
|
| 1286 | dict_type = self.types[o]
|
| 1287 | # Note: need a lookup function that understands DictExpr -> Instance
|
| 1288 | assert isinstance(dict_type, Instance), dict_type
|
| 1289 |
|
| 1290 | c_type = GetCType(dict_type)
|
| 1291 | assert c_type.endswith('*'), c_type
|
| 1292 | c_type = c_type[:-1] # HACK TO CLEAN UP
|
| 1293 |
|
| 1294 | key_type, val_type = dict_type.args
|
| 1295 | key_c_type = GetCType(key_type)
|
| 1296 | val_c_type = GetCType(val_type)
|
| 1297 |
|
| 1298 | self.write('Alloc<%s>(' % c_type)
|
| 1299 | #self.write('NewDict<%s, %s>(' % (key_c_type, val_c_type))
|
| 1300 | if o.items:
|
| 1301 | keys = [k for k, _ in o.items]
|
| 1302 | values = [v for _, v in o.items]
|
| 1303 |
|
| 1304 | self.write('std::initializer_list<%s>' % key_c_type)
|
| 1305 | self._WriteListElements(keys)
|
| 1306 | self.write(', ')
|
| 1307 |
|
| 1308 | self.write('std::initializer_list<%s>' % val_c_type)
|
| 1309 | self._WriteListElements(values)
|
| 1310 |
|
| 1311 | self.write(')')
|
| 1312 |
|
| 1313 | def visit_tuple_expr(self, o: 'mypy.nodes.TupleExpr') -> None:
|
| 1314 | tuple_type = self.types[o]
|
| 1315 | c_type = GetCType(tuple_type)
|
| 1316 | assert c_type.endswith('*'), c_type
|
| 1317 | c_type = c_type[:-1] # HACK TO CLEAN UP
|
| 1318 |
|
| 1319 | self.write('(Alloc<%s>(' % c_type)
|
| 1320 | for i, item in enumerate(o.items):
|
| 1321 | if i != 0:
|
| 1322 | self.write(', ')
|
| 1323 | self.accept(item)
|
| 1324 | self.write('))')
|
| 1325 |
|
| 1326 | def visit_index_expr(self, o: 'mypy.nodes.IndexExpr') -> None:
|
| 1327 | self.accept(o.base)
|
| 1328 |
|
| 1329 | #base_type = self.types[o.base]
|
| 1330 | #self.log('*** BASE TYPE %s', base_type)
|
| 1331 |
|
| 1332 | if isinstance(o.index, SliceExpr):
|
| 1333 | self.accept(o.index) # method call
|
| 1334 | else:
|
| 1335 | # it's hard syntactically to do (*a)[0], so do it this way.
|
| 1336 | if util.SMALL_STR:
|
| 1337 | self.write('.at(')
|
| 1338 | else:
|
| 1339 | self.write('->at(')
|
| 1340 |
|
| 1341 | self.accept(o.index)
|
| 1342 | self.write(')')
|
| 1343 |
|
| 1344 | def visit_slice_expr(self, o: 'mypy.nodes.SliceExpr') -> None:
|
| 1345 | self.write('->slice(')
|
| 1346 | if o.begin_index:
|
| 1347 | self.accept(o.begin_index)
|
| 1348 | else:
|
| 1349 | self.write('0') # implicit beginning
|
| 1350 |
|
| 1351 | if o.end_index:
|
| 1352 | self.write(', ')
|
| 1353 | self.accept(o.end_index)
|
| 1354 |
|
| 1355 | if o.stride:
|
| 1356 | if not o.begin_index or not o.end_index:
|
| 1357 | raise AssertionError(
|
| 1358 | 'Stride only supported with beginning and ending index')
|
| 1359 |
|
| 1360 | self.write(', ')
|
| 1361 | self.accept(o.stride)
|
| 1362 |
|
| 1363 | self.write(')')
|
| 1364 |
|
| 1365 | def visit_conditional_expr(self, o: 'mypy.nodes.ConditionalExpr') -> None:
|
| 1366 | if not _CheckCondition(o.cond, self.types):
|
| 1367 | self.report_error(
|
| 1368 | o,
|
| 1369 | "Use explicit len(obj) or 'obj is not None' for mystr, mylist, mydict"
|
| 1370 | )
|
| 1371 | return
|
| 1372 |
|
| 1373 | # 0 if b else 1 -> b ? 0 : 1
|
| 1374 | self.accept(o.cond)
|
| 1375 | self.write(' ? ')
|
| 1376 | self.accept(o.if_expr)
|
| 1377 | self.write(' : ')
|
| 1378 | self.accept(o.else_expr)
|
| 1379 |
|
| 1380 | def _WriteTupleUnpacking(self,
|
| 1381 | temp_name: str,
|
| 1382 | lval_items: List[Expression],
|
| 1383 | item_types: List[Type],
|
| 1384 | is_return: bool = False) -> None:
|
| 1385 | """Used by assignment and for loops.
|
| 1386 |
|
| 1387 | is_return is a special case for:
|
| 1388 |
|
| 1389 | # return Tuple2<A, B> by VALUE, not Tuple2<A, B>* pointer
|
| 1390 | a, b = myfunc()
|
| 1391 | """
|
| 1392 | for i, (lval_item, item_type) in enumerate(zip(lval_items,
|
| 1393 | item_types)):
|
| 1394 | if isinstance(lval_item, NameExpr):
|
| 1395 | if util.SkipAssignment(lval_item.name):
|
| 1396 | continue
|
| 1397 | self.write_ind('%s', lval_item.name)
|
| 1398 | else:
|
| 1399 | # Could be MemberExpr like self.foo, self.bar = baz
|
| 1400 | self.write_ind('')
|
| 1401 | self.accept(lval_item)
|
| 1402 |
|
| 1403 | # Tuples that are return values aren't pointers
|
| 1404 | op = '.' if is_return else '->'
|
| 1405 | self.write(' = %s%sat%d();\n', temp_name, op, i) # RHS
|
| 1406 |
|
| 1407 | def _WriteTupleUnpackingInLoop(self, temp_name: str,
|
| 1408 | lval_items: List[Expression],
|
| 1409 | item_types: List[Type]) -> None:
|
| 1410 | for i, (lval_item, item_type) in enumerate(zip(lval_items,
|
| 1411 | item_types)):
|
| 1412 | c_item_type = GetCType(item_type)
|
| 1413 |
|
| 1414 | if isinstance(lval_item, NameExpr):
|
| 1415 | if util.SkipAssignment(lval_item.name):
|
| 1416 | continue
|
| 1417 |
|
| 1418 | self.write_ind('%s %s', c_item_type, lval_item.name)
|
| 1419 | else:
|
| 1420 | # Could be MemberExpr like self.foo, self.bar = baz
|
| 1421 | self.write_ind('')
|
| 1422 | self.accept(lval_item)
|
| 1423 |
|
| 1424 | op = '->'
|
| 1425 | self.write(' = %s%sat%d();\n', temp_name, op, i) # RHS
|
| 1426 |
|
| 1427 | # Note: it would be nice to eliminate these roots, just like
|
| 1428 | # StackRoots _for() below
|
| 1429 | if isinstance(lval_item, NameExpr):
|
| 1430 | if CTypeIsManaged(c_item_type) and not self.stack_roots:
|
| 1431 | self.write_ind('StackRoot _unpack_%d(&%s);\n' %
|
| 1432 | (i, lval_item.name))
|
| 1433 |
|
| 1434 | def _AssignNewDictImpl(self, lval: Expression, prefix: str = '') -> None:
|
| 1435 | """Translate NewDict() -> Alloc<Dict<K, V>>
|
| 1436 |
|
| 1437 | This function is a specal case because the RHS need TYPES from the LHS.
|
| 1438 |
|
| 1439 | e.g. here is how we make ORDERED dictionaries, which can't be done with {}:
|
| 1440 |
|
| 1441 | d = NewDict() # type: Dict[int, int]
|
| 1442 |
|
| 1443 | -> one of
|
| 1444 |
|
| 1445 | auto* d = Alloc<Dict<int, int>>(); # declare
|
| 1446 | d = Alloc<Dict<int, int>>(); # mutate
|
| 1447 |
|
| 1448 | We also have:
|
| 1449 |
|
| 1450 | self.d = NewDict()
|
| 1451 | ->
|
| 1452 | this->d = Alloc<Dict<int, int>)();
|
| 1453 | """
|
| 1454 | lval_type = self.types[lval]
|
| 1455 | #self.log('lval type %s', lval_type)
|
| 1456 |
|
| 1457 | # Fix for Dict[str, value]? in ASDL
|
| 1458 | if (isinstance(lval_type, UnionType) and len(lval_type.items) == 2 and
|
| 1459 | isinstance(lval_type.items[1], NoneTyp)):
|
| 1460 | lval_type = lval_type.items[0]
|
| 1461 |
|
| 1462 | c_type = GetCType(lval_type)
|
| 1463 | assert c_type.endswith('*')
|
| 1464 | self.write('Alloc<%s>()', c_type[:-1])
|
| 1465 |
|
| 1466 | def _AssignCastImpl(self, lval: Expression, rval: CallExpr) -> None:
|
| 1467 | """
|
| 1468 | is_downcast_and_shadow idiom:
|
| 1469 |
|
| 1470 | src = cast(source__SourcedFile, UP_src)
|
| 1471 | -> source__SourcedFile* src = static_cast<source__SourcedFile>(UP_src)
|
| 1472 | """
|
| 1473 | assert isinstance(lval, NameExpr)
|
| 1474 | type_expr = rval.args[0]
|
| 1475 | subtype_name = _GetCTypeForCast(type_expr)
|
| 1476 |
|
| 1477 | cast_kind = _GetCastKind(self.module_path, subtype_name)
|
| 1478 |
|
| 1479 | is_downcast_and_shadow = False
|
| 1480 | to_cast = rval.args[1]
|
| 1481 | if isinstance(to_cast, NameExpr):
|
| 1482 | if to_cast.name.startswith('UP_'):
|
| 1483 | is_downcast_and_shadow = True
|
| 1484 |
|
| 1485 | if is_downcast_and_shadow:
|
| 1486 | # Declare NEW local variable inside case, which shadows it
|
| 1487 | self.write_ind('%s %s = %s<%s>(', subtype_name, lval.name,
|
| 1488 | cast_kind, subtype_name)
|
| 1489 | else:
|
| 1490 | # Normal variable
|
| 1491 | self.write_ind('%s = %s<%s>(', lval.name, cast_kind, subtype_name)
|
| 1492 |
|
| 1493 | self.accept(rval.args[1]) # variable being casted
|
| 1494 | self.write(');\n')
|
| 1495 |
|
| 1496 | def _AssignToGenerator(self, o: 'mypy.nodes.AssignmentStmt',
|
| 1497 | lval: Expression, rval_type: Instance) -> None:
|
| 1498 | """
|
| 1499 | it_f = f(42)
|
| 1500 |
|
| 1501 | translates to
|
| 1502 |
|
| 1503 | List<int> _iter_buf_it;
|
| 1504 | f(42, &_iter_buf_it);
|
| 1505 | """
|
| 1506 | # We're calling a generator. Create a temporary List<T> on the stack
|
| 1507 | # to accumulate the results in one big batch, then wrap it in
|
| 1508 | # ListIter<T>.
|
| 1509 | assert len(rval_type.args) == 1, rval_type.args
|
| 1510 | c_type = GetCType(rval_type)
|
| 1511 |
|
| 1512 | type_param = rval_type.args[0]
|
| 1513 | inner_c_type = GetCType(type_param)
|
| 1514 |
|
| 1515 | assert isinstance(lval, NameExpr), lval
|
| 1516 | eager_list_name = 'YIELD_%s' % lval.name
|
| 1517 | eager_list_type = 'List<%s>*' % inner_c_type
|
| 1518 |
|
| 1519 | # write the variable to accumulate into
|
| 1520 | self.write_ind('List<%s> %s;\n', inner_c_type, eager_list_name)
|
| 1521 |
|
| 1522 | # AssignmentStmt key, like:
|
| 1523 | # it_f = f()
|
| 1524 | # maybe call them self.generator_func, generator_assign
|
| 1525 | # In MyPy, the type is Iterator though
|
| 1526 | self.yield_eager_assign[o] = (eager_list_name, eager_list_type)
|
| 1527 | self.write_ind('')
|
| 1528 |
|
| 1529 | self.yield_assign_node = o # AssignmentStmt
|
| 1530 | self.accept(o.rvalue)
|
| 1531 | self.yield_assign_node = None
|
| 1532 |
|
| 1533 | self.write(';\n')
|
| 1534 |
|
| 1535 | self.write_ind('%s %s(&%s);\n', c_type, lval.name, eager_list_name)
|
| 1536 |
|
| 1537 | def oils_visit_assign_to_listcomp(self, lval: NameExpr,
|
| 1538 | left_expr: Expression,
|
| 1539 | index_expr: Expression, seq: Expression,
|
| 1540 | cond: Expression) -> None:
|
| 1541 | """
|
| 1542 | Special case for list comprehensions. Note that the LHS MUST be on the
|
| 1543 | LHS, so we can append to it.
|
| 1544 |
|
| 1545 | y = [i+1 for i in x[1:] if i]
|
| 1546 | =>
|
| 1547 | y = []
|
| 1548 | for i in x[1:]:
|
| 1549 | if i:
|
| 1550 | y.append(i+1)
|
| 1551 | (but in C++)
|
| 1552 | """
|
| 1553 | self.write_ind('%s = ', lval.name)
|
| 1554 |
|
| 1555 | # BUG: can't use this to filter
|
| 1556 | # results = [x for x in results]
|
| 1557 | if isinstance(seq, NameExpr) and seq.name == lval.name:
|
| 1558 | raise AssertionError(
|
| 1559 | "Can't use var %r in list comprehension because it would "
|
| 1560 | "be overwritten" % lval.name)
|
| 1561 |
|
| 1562 | c_type = GetCType(self.types[lval])
|
| 1563 | # Write empty container as initialization.
|
| 1564 | assert c_type.endswith('*'), c_type # Hack
|
| 1565 | self.write('Alloc<%s>();\n' % c_type[:-1])
|
| 1566 |
|
| 1567 | over_type = self.types[seq]
|
| 1568 | assert isinstance(over_type, Instance), over_type
|
| 1569 |
|
| 1570 | if over_type.type.fullname == 'builtins.list':
|
| 1571 | c_type = GetCType(over_type)
|
| 1572 | # remove *
|
| 1573 | assert c_type.endswith('*'), c_type
|
| 1574 | c_iter_type = c_type.replace('List', 'ListIter', 1)[:-1]
|
| 1575 | else:
|
| 1576 | # List comprehension over dictionary not implemented
|
| 1577 | c_iter_type = 'TODO_DICT'
|
| 1578 |
|
| 1579 | self.write_ind('for (%s it(', c_iter_type)
|
| 1580 | self.accept(seq)
|
| 1581 | self.write('); !it.Done(); it.Next()) {\n')
|
| 1582 |
|
| 1583 | item_type = over_type.args[0] # get 'int' from 'List<int>'
|
| 1584 |
|
| 1585 | if isinstance(item_type, Instance):
|
| 1586 | self.write_ind(' %s ', GetCType(item_type))
|
| 1587 | # TODO(StackRoots): for ch in 'abc'
|
| 1588 | self.accept(index_expr)
|
| 1589 | self.write(' = it.Value();\n')
|
| 1590 |
|
| 1591 | elif isinstance(item_type, TupleType): # [x for x, y in pairs]
|
| 1592 | c_item_type = GetCType(item_type)
|
| 1593 |
|
| 1594 | if isinstance(index_expr, TupleExpr):
|
| 1595 | temp_name = 'tup%d' % self.unique_id
|
| 1596 | self.unique_id += 1
|
| 1597 | self.write_ind(' %s %s = it.Value();\n', c_item_type,
|
| 1598 | temp_name)
|
| 1599 |
|
| 1600 | self.indent += 1
|
| 1601 |
|
| 1602 | # list comp
|
| 1603 | self._WriteTupleUnpackingInLoop(temp_name, index_expr.items,
|
| 1604 | item_type.items)
|
| 1605 |
|
| 1606 | self.indent -= 1
|
| 1607 | else:
|
| 1608 | raise AssertionError()
|
| 1609 |
|
| 1610 | else:
|
| 1611 | raise AssertionError('Unexpected type %s' % item_type)
|
| 1612 |
|
| 1613 | if cond is not None:
|
| 1614 | self.indent += 1
|
| 1615 | self.write_ind('if (')
|
| 1616 | self.accept(cond)
|
| 1617 | self.write(') {\n')
|
| 1618 |
|
| 1619 | self.write_ind(' %s->append(', lval.name)
|
| 1620 | self.accept(left_expr)
|
| 1621 | self.write(');\n')
|
| 1622 |
|
| 1623 | if cond:
|
| 1624 | self.write_ind('}\n')
|
| 1625 | self.indent -= 1
|
| 1626 |
|
| 1627 | self.write_ind('}\n')
|
| 1628 |
|
| 1629 | def oils_visit_assignment_stmt(self, o: 'mypy.nodes.AssignmentStmt',
|
| 1630 | lval: Expression, rval: Expression) -> None:
|
| 1631 |
|
| 1632 | # GLOBAL CONSTANTS - Avoid Alloc<T>, since that can't be done until main().
|
| 1633 | if self.indent == 0:
|
| 1634 | assert isinstance(lval, NameExpr), lval
|
| 1635 | if util.SkipAssignment(lval.name):
|
| 1636 | return
|
| 1637 | #self.log(' GLOBAL: %s', lval.name)
|
| 1638 |
|
| 1639 | lval_type = self.types[lval]
|
| 1640 |
|
| 1641 | # Global
|
| 1642 | # L = [1, 2] # type: List[int]
|
| 1643 | if isinstance(rval, ListExpr):
|
| 1644 | assert isinstance(lval_type, Instance), lval_type
|
| 1645 |
|
| 1646 | item_type = lval_type.args[0]
|
| 1647 | item_c_type = GetCType(item_type)
|
| 1648 |
|
| 1649 | # Any constant strings will have already been written
|
| 1650 | # TODO: Assert that every item is a constant?
|
| 1651 | self.write('GLOBAL_LIST(%s, %s, %d, ', lval.name, item_c_type,
|
| 1652 | len(rval.items))
|
| 1653 |
|
| 1654 | self._WriteListElements(rval.items, sep=' COMMA ')
|
| 1655 |
|
| 1656 | self.write(');\n')
|
| 1657 | return
|
| 1658 |
|
| 1659 | # Global
|
| 1660 | # D = {"foo": "bar"} # type: Dict[str, str]
|
| 1661 | if isinstance(rval, DictExpr):
|
| 1662 | assert isinstance(lval_type, Instance), lval_type
|
| 1663 |
|
| 1664 | key_type, val_type = lval_type.args
|
| 1665 |
|
| 1666 | key_c_type = GetCType(key_type)
|
| 1667 | val_c_type = GetCType(val_type)
|
| 1668 |
|
| 1669 | dict_expr = rval
|
| 1670 | self.write('GLOBAL_DICT(%s, %s, %s, %d, ', lval.name,
|
| 1671 | key_c_type, val_c_type, len(dict_expr.items))
|
| 1672 |
|
| 1673 | keys = [k for k, _ in dict_expr.items]
|
| 1674 | values = [v for _, v in dict_expr.items]
|
| 1675 |
|
| 1676 | self._WriteListElements(keys, sep=' COMMA ')
|
| 1677 | self.write(', ')
|
| 1678 | self._WriteListElements(values, sep=' COMMA ')
|
| 1679 |
|
| 1680 | self.write(');\n')
|
| 1681 | return
|
| 1682 |
|
| 1683 | # We could do GcGlobal<> for ASDL classes, but Oils doesn't use them
|
| 1684 | if isinstance(rval, CallExpr):
|
| 1685 | self.report_error(
|
| 1686 | o,
|
| 1687 | "Can't initialize objects at the top level, only BigStr List Dict"
|
| 1688 | )
|
| 1689 | return
|
| 1690 |
|
| 1691 | # myconst = 1 << 3 => myconst = 1 << 3 is currently allowed
|
| 1692 |
|
| 1693 | #
|
| 1694 | # Non-top-level
|
| 1695 | #
|
| 1696 |
|
| 1697 | if isinstance(rval, CallExpr):
|
| 1698 | callee = rval.callee
|
| 1699 | callee_name = callee.name
|
| 1700 |
|
| 1701 | if callee_name == 'NewDict':
|
| 1702 | self.write_ind('')
|
| 1703 |
|
| 1704 | # Hack for non-members - why does this work?
|
| 1705 | # Tests cases in mycpp/examples/containers.py
|
| 1706 | if (not isinstance(lval, MemberExpr) and
|
| 1707 | self.current_func_node is None):
|
| 1708 | self.write('auto* ')
|
| 1709 |
|
| 1710 | self.accept(lval)
|
| 1711 | self.write(' = ')
|
| 1712 | self._AssignNewDictImpl(lval) # uses lval, not rval
|
| 1713 | self.write(';\n')
|
| 1714 | return
|
| 1715 |
|
| 1716 | if callee_name == 'cast':
|
| 1717 | self._AssignCastImpl(lval, rval)
|
| 1718 | return
|
| 1719 |
|
| 1720 | rval_type = self.types[rval]
|
| 1721 | if (isinstance(rval_type, Instance) and
|
| 1722 | rval_type.type.fullname == 'typing.Iterator'):
|
| 1723 | self._AssignToGenerator(o, lval, rval_type)
|
| 1724 | return
|
| 1725 |
|
| 1726 | if isinstance(lval, NameExpr):
|
| 1727 | lval_type = self.types[lval]
|
| 1728 | #c_type = GetCType(lval_type, local=self.indent != 0)
|
| 1729 | c_type = GetCType(lval_type)
|
| 1730 |
|
| 1731 | if self.at_global_scope:
|
| 1732 | # globals always get a type -- they're not mutated
|
| 1733 | self.write_ind('%s %s = ', c_type, lval.name)
|
| 1734 | else:
|
| 1735 | # local declarations are "hoisted" to the top of the function
|
| 1736 | self.write_ind('%s = ', lval.name)
|
| 1737 |
|
| 1738 | self.accept(rval)
|
| 1739 | self.write(';\n')
|
| 1740 | return
|
| 1741 |
|
| 1742 | if isinstance(lval, MemberExpr): # self.x = foo
|
| 1743 | self.write_ind('')
|
| 1744 | self.accept(lval)
|
| 1745 | self.write(' = ')
|
| 1746 | self.accept(rval)
|
| 1747 | self.write(';\n')
|
| 1748 | return
|
| 1749 |
|
| 1750 | if isinstance(lval, IndexExpr): # a[x] = 1
|
| 1751 | # d->set(x, 1) for both List and Dict
|
| 1752 | self.write_ind('')
|
| 1753 | self.accept(lval.base)
|
| 1754 | self.write('->set(')
|
| 1755 | self.accept(lval.index)
|
| 1756 | self.write(', ')
|
| 1757 | self.accept(rval)
|
| 1758 | self.write(');\n')
|
| 1759 | return
|
| 1760 |
|
| 1761 | if isinstance(lval, TupleExpr):
|
| 1762 | # An assignment to an n-tuple turns into n+1 statements. Example:
|
| 1763 | #
|
| 1764 | # x, y = mytuple
|
| 1765 | #
|
| 1766 | # Tuple2<int, BigStr*> tup1 = mytuple
|
| 1767 | # int x = tup1->at0()
|
| 1768 | # BigStr* y = tup1->at1()
|
| 1769 |
|
| 1770 | rvalue_type = self.types[rval]
|
| 1771 |
|
| 1772 | # type alias upgrade for MyPy 0.780
|
| 1773 | if isinstance(rvalue_type, TypeAliasType):
|
| 1774 | rvalue_type = rvalue_type.alias.target
|
| 1775 |
|
| 1776 | assert isinstance(rvalue_type, TupleType), rvalue_type
|
| 1777 |
|
| 1778 | c_type = GetCType(rvalue_type)
|
| 1779 |
|
| 1780 | is_return = (isinstance(rval, CallExpr) and
|
| 1781 | rval.callee.name != "next")
|
| 1782 | if is_return:
|
| 1783 | assert c_type.endswith('*')
|
| 1784 | c_type = c_type[:-1]
|
| 1785 |
|
| 1786 | temp_name = 'tup%d' % self.unique_id
|
| 1787 | self.unique_id += 1
|
| 1788 | self.write_ind('%s %s = ', c_type, temp_name)
|
| 1789 |
|
| 1790 | self.accept(rval)
|
| 1791 | self.write(';\n')
|
| 1792 |
|
| 1793 | # assignment
|
| 1794 | self._WriteTupleUnpacking(temp_name,
|
| 1795 | lval.items,
|
| 1796 | rvalue_type.items,
|
| 1797 | is_return=is_return)
|
| 1798 | return
|
| 1799 |
|
| 1800 | raise AssertionError(lval)
|
| 1801 |
|
| 1802 | def _WriteBody(self, body: List[Statement]) -> None:
|
| 1803 | """Write a block without the { }."""
|
| 1804 | for stmt in body:
|
| 1805 | self.accept(stmt)
|
| 1806 |
|
| 1807 | def oils_visit_for_stmt(self, o: 'mypy.nodes.ForStmt',
|
| 1808 | func_name: Optional[str]) -> None:
|
| 1809 | if 0:
|
| 1810 | self.log('ForStmt')
|
| 1811 | self.log(' index_type %s', o.index_type)
|
| 1812 | self.log(' inferred_item_type %s', o.inferred_item_type)
|
| 1813 | self.log(' inferred_iterator_type %s', o.inferred_iterator_type)
|
| 1814 |
|
| 1815 | if func_name:
|
| 1816 | assert isinstance(o.expr, CallExpr), o.expr # caller ensured it
|
| 1817 | args = o.expr.args
|
| 1818 |
|
| 1819 | # special case: 'for i in xrange(3)'
|
| 1820 | if func_name == 'xrange':
|
| 1821 | assert isinstance(o.index, NameExpr), o.index
|
| 1822 | index_name = o.index.name
|
| 1823 |
|
| 1824 | assert isinstance(o.expr, CallExpr), o.expr # caller ensured it
|
| 1825 | num_args = len(args)
|
| 1826 |
|
| 1827 | if num_args == 1: # xrange(end)
|
| 1828 | self.write_ind('for (int %s = 0; %s < ', index_name,
|
| 1829 | index_name)
|
| 1830 | self.accept(args[0])
|
| 1831 | self.write('; ++%s) ', index_name)
|
| 1832 |
|
| 1833 | elif num_args == 2: # xrange(being, end)
|
| 1834 | self.write_ind('for (int %s = ', index_name)
|
| 1835 | self.accept(args[0])
|
| 1836 | self.write('; %s < ', index_name)
|
| 1837 | self.accept(args[1])
|
| 1838 | self.write('; ++%s) ', index_name)
|
| 1839 |
|
| 1840 | elif num_args == 3: # xrange(being, end, step)
|
| 1841 | # Special case to detect a step of -1. This is a static
|
| 1842 | # heuristic, because it could be negative dynamically.
|
| 1843 | # TODO: could add an API like mylib.reverse_xrange()
|
| 1844 | step = args[2]
|
| 1845 | if isinstance(step, UnaryExpr) and step.op == '-':
|
| 1846 | comparison_op = '>'
|
| 1847 | else:
|
| 1848 | comparison_op = '<'
|
| 1849 |
|
| 1850 | self.write_ind('for (int %s = ', index_name)
|
| 1851 | self.accept(args[0])
|
| 1852 | self.write('; %s %s ', index_name, comparison_op)
|
| 1853 | self.accept(args[1])
|
| 1854 | self.write('; %s += ', index_name)
|
| 1855 | self.accept(step)
|
| 1856 | self.write(') ')
|
| 1857 |
|
| 1858 | else:
|
| 1859 | raise AssertionError()
|
| 1860 |
|
| 1861 | self.accept(o.body)
|
| 1862 | return
|
| 1863 |
|
| 1864 | reverse = False
|
| 1865 |
|
| 1866 | # for i, x in enumerate(...):
|
| 1867 | index0_name = None
|
| 1868 | if func_name == 'enumerate':
|
| 1869 | assert isinstance(o.index, TupleExpr), o.index
|
| 1870 | index0 = o.index.items[0]
|
| 1871 |
|
| 1872 | assert isinstance(index0, NameExpr), index0
|
| 1873 | index0_name = index0.name # generate int i = 0; ; ++i
|
| 1874 |
|
| 1875 | # Get type of 'x' in 'for i, x in enumerate(...)'
|
| 1876 | assert isinstance(o.inferred_item_type,
|
| 1877 | TupleType), o.inferred_item_type
|
| 1878 | item_type = o.inferred_item_type.items[1]
|
| 1879 | index_expr = o.index.items[1]
|
| 1880 |
|
| 1881 | assert isinstance(o.expr, CallExpr), o.expr # caller ensured
|
| 1882 | # enumerate(mylist) turns into iteration over mylist with variable i
|
| 1883 | assert len(args) == 1, args
|
| 1884 | iterated_over = args[0]
|
| 1885 |
|
| 1886 | elif func_name == 'reversed':
|
| 1887 | # NOTE: enumerate() and reversed() can't be mixed yet. But you CAN
|
| 1888 | # reverse iter over tuples.
|
| 1889 | item_type = o.inferred_item_type
|
| 1890 | index_expr = o.index
|
| 1891 |
|
| 1892 | assert len(args) == 1, args
|
| 1893 | iterated_over = args[0]
|
| 1894 |
|
| 1895 | reverse = True # use different iterate
|
| 1896 |
|
| 1897 | elif func_name == 'iteritems':
|
| 1898 | item_type = o.inferred_item_type
|
| 1899 | index_expr = o.index
|
| 1900 |
|
| 1901 | assert len(args) == 1, args
|
| 1902 | # This should be a dict
|
| 1903 | iterated_over = args[0]
|
| 1904 |
|
| 1905 | #log('------------ ITERITEMS OVER %s', iterated_over)
|
| 1906 |
|
| 1907 | else:
|
| 1908 | item_type = o.inferred_item_type
|
| 1909 | index_expr = o.index
|
| 1910 | iterated_over = o.expr
|
| 1911 |
|
| 1912 | over_type = self.types[iterated_over]
|
| 1913 |
|
| 1914 | if isinstance(over_type, TypeAliasType):
|
| 1915 | over_type = over_type.alias.target
|
| 1916 |
|
| 1917 | assert isinstance(over_type, Instance), over_type
|
| 1918 |
|
| 1919 | if 0:
|
| 1920 | log("***** OVER %s %s", over_type, dir(over_type))
|
| 1921 | t = over_type.type
|
| 1922 | log("***** t %s %s", t, dir(t))
|
| 1923 | bases = t.bases
|
| 1924 | # Look for string and dict!
|
| 1925 | log("=== bases %s %s", bases, dir(bases))
|
| 1926 |
|
| 1927 | #self.log(' iterating over type %s', over_type)
|
| 1928 | #self.log(' iterating over type %s', over_type.type.fullname)
|
| 1929 |
|
| 1930 | eager_list_name: Optional[str] = None
|
| 1931 |
|
| 1932 | over_list = False
|
| 1933 | over_dict = False
|
| 1934 |
|
| 1935 | if over_type.type.fullname == 'builtins.list':
|
| 1936 | over_list = True
|
| 1937 | container_base_type = over_type
|
| 1938 |
|
| 1939 | if over_type.type.fullname == 'builtins.dict':
|
| 1940 | over_dict = True
|
| 1941 | container_base_type = over_type
|
| 1942 |
|
| 1943 | # now check base classes
|
| 1944 | for base_type in over_type.type.bases:
|
| 1945 | n = base_type.type.fullname
|
| 1946 | if n == 'builtins.list':
|
| 1947 | over_list = True
|
| 1948 | container_base_type = base_type
|
| 1949 | elif n == 'builtins.dict':
|
| 1950 | over_dict = True
|
| 1951 | container_base_type = base_type
|
| 1952 |
|
| 1953 | assert not (over_dict and over_list)
|
| 1954 |
|
| 1955 | if over_list:
|
| 1956 | c_type = GetCType(over_type)
|
| 1957 | assert c_type.endswith('*'), c_type
|
| 1958 | inner_c_type = GetCType(container_base_type.args[0])
|
| 1959 | c_iter_type = 'ListIter<%s>' % inner_c_type
|
| 1960 |
|
| 1961 | # ReverseListIter!
|
| 1962 | if reverse:
|
| 1963 | c_iter_type = 'Reverse' + c_iter_type
|
| 1964 |
|
| 1965 | elif over_dict:
|
| 1966 | key_c_type = GetCType(container_base_type.args[0])
|
| 1967 | val_c_type = GetCType(container_base_type.args[1])
|
| 1968 | c_iter_type = 'DictIter<%s, %s>' % (key_c_type, val_c_type)
|
| 1969 | assert not reverse
|
| 1970 |
|
| 1971 | elif over_type.type.fullname == 'builtins.str':
|
| 1972 | c_iter_type = 'StrIter'
|
| 1973 | assert not reverse # can't reverse iterate over string yet
|
| 1974 |
|
| 1975 | elif over_type.type.fullname == 'typing.Iterator':
|
| 1976 | # We're iterating over a generator. Create a temporary List<T> on
|
| 1977 | # the stack to accumulate the results in one big batch.
|
| 1978 | c_iter_type = GetCType(over_type)
|
| 1979 |
|
| 1980 | assert len(over_type.args) == 1, over_type.args
|
| 1981 | inner_c_type = GetCType(over_type.args[0])
|
| 1982 |
|
| 1983 | # eager_list_name is used below
|
| 1984 | eager_list_name = 'YIELD_for_%d' % self.unique_id
|
| 1985 | eager_list_type = 'List<%s>*' % inner_c_type
|
| 1986 | self.unique_id += 1
|
| 1987 |
|
| 1988 | self.write_ind('List<%s> %s;\n', inner_c_type, eager_list_name)
|
| 1989 | self.write_ind('')
|
| 1990 |
|
| 1991 | # ForStmt - could be self.generator_for_stmt
|
| 1992 | #
|
| 1993 | # for x in my_generator(42):
|
| 1994 | # log('x = %s', x)
|
| 1995 | #
|
| 1996 | # Turns into
|
| 1997 | # List<T> _for_yield_acc3;
|
| 1998 | # my_generator(42, &_for_yield_acc3);
|
| 1999 | # for (ListIter it(_for_yield_acc3) ...)
|
| 2000 |
|
| 2001 | self.yield_eager_for[o] = (eager_list_name, eager_list_type)
|
| 2002 |
|
| 2003 | self.yield_for_node = o # ForStmt
|
| 2004 | self.accept(iterated_over)
|
| 2005 | self.yield_for_node = None
|
| 2006 |
|
| 2007 | self.write(';\n')
|
| 2008 |
|
| 2009 | else: # assume it's like d.iteritems()? Iterator type
|
| 2010 | assert False, over_type
|
| 2011 |
|
| 2012 | if index0_name:
|
| 2013 | # can't initialize two things in a for loop, so do it on a separate line
|
| 2014 | self.write_ind('%s = 0;\n', index0_name)
|
| 2015 | index_update = ', ++%s' % index0_name
|
| 2016 | else:
|
| 2017 | index_update = ''
|
| 2018 |
|
| 2019 | self.write_ind('for (%s it(', c_iter_type)
|
| 2020 | if eager_list_name:
|
| 2021 | self.write('&%s', eager_list_name)
|
| 2022 | else:
|
| 2023 | self.accept(iterated_over) # the thing being iterated over
|
| 2024 | self.write('); !it.Done(); it.Next()%s) {\n', index_update)
|
| 2025 |
|
| 2026 | # for x in it: ...
|
| 2027 | # for i, x in enumerate(pairs): ...
|
| 2028 |
|
| 2029 | if isinstance(item_type, Instance) or index0_name:
|
| 2030 | c_item_type = GetCType(item_type)
|
| 2031 | self.write_ind(' %s ', c_item_type)
|
| 2032 | self.accept(index_expr)
|
| 2033 | if over_dict:
|
| 2034 | self.write(' = it.Key();\n')
|
| 2035 | else:
|
| 2036 | self.write(' = it.Value();\n')
|
| 2037 |
|
| 2038 | # Register loop variable as a stack root.
|
| 2039 | # Note we have mylib.Collect() in CommandEvaluator::_Execute(), and
|
| 2040 | # it's called in a loop by _ExecuteList(). Although the 'child'
|
| 2041 | # variable is already live by other means.
|
| 2042 | # TODO: Test how much this affects performance.
|
| 2043 | if CTypeIsManaged(c_item_type) and not self.stack_roots:
|
| 2044 | self.write_ind(' StackRoot _for(&')
|
| 2045 | self.accept(index_expr)
|
| 2046 | self.write_ind(');\n')
|
| 2047 |
|
| 2048 | elif isinstance(item_type, TupleType): # for x, y in pairs
|
| 2049 | if over_dict:
|
| 2050 | assert isinstance(o.index, TupleExpr), o.index
|
| 2051 | index_items = o.index.items
|
| 2052 | assert len(index_items) == 2, index_items
|
| 2053 | assert len(item_type.items) == 2, item_type.items
|
| 2054 |
|
| 2055 | key_type = GetCType(item_type.items[0])
|
| 2056 | val_type = GetCType(item_type.items[1])
|
| 2057 |
|
| 2058 | #log('** %s key_type %s', item_type.items[0], key_type)
|
| 2059 | #log('** %s val_type %s', item_type.items[1], val_type)
|
| 2060 |
|
| 2061 | assert isinstance(index_items[0], NameExpr), index_items[0]
|
| 2062 | assert isinstance(index_items[1], NameExpr), index_items[1]
|
| 2063 |
|
| 2064 | # TODO(StackRoots): k, v
|
| 2065 | self.write_ind(' %s %s = it.Key();\n', key_type,
|
| 2066 | index_items[0].name)
|
| 2067 | self.write_ind(' %s %s = it.Value();\n', val_type,
|
| 2068 | index_items[1].name)
|
| 2069 |
|
| 2070 | else:
|
| 2071 | # Example:
|
| 2072 | # for (ListIter it(mylist); !it.Done(); it.Next()) {
|
| 2073 | # Tuple2<int, BigStr*> tup1 = it.Value();
|
| 2074 | # int i = tup1->at0();
|
| 2075 | # BigStr* s = tup1->at1();
|
| 2076 | # log("%d %s", i, s);
|
| 2077 | # }
|
| 2078 |
|
| 2079 | c_item_type = GetCType(item_type)
|
| 2080 |
|
| 2081 | if isinstance(o.index, TupleExpr):
|
| 2082 | # TODO(StackRoots)
|
| 2083 | temp_name = 'tup%d' % self.unique_id
|
| 2084 | self.unique_id += 1
|
| 2085 | self.write_ind(' %s %s = it.Value();\n', c_item_type,
|
| 2086 | temp_name)
|
| 2087 |
|
| 2088 | # loop - for x, y in other:
|
| 2089 | self.indent += 1
|
| 2090 | self._WriteTupleUnpackingInLoop(temp_name, o.index.items,
|
| 2091 | item_type.items)
|
| 2092 | self.indent -= 1
|
| 2093 |
|
| 2094 | elif isinstance(o.index, NameExpr):
|
| 2095 | self.write_ind(' %s %s = it.Value();\n', c_item_type,
|
| 2096 | o.index.name)
|
| 2097 | #self.write_ind(' StackRoots _for(&%s)\n;', o.index.name)
|
| 2098 |
|
| 2099 | else:
|
| 2100 | raise AssertionError()
|
| 2101 |
|
| 2102 | else:
|
| 2103 | raise AssertionError('Unexpected type %s' % item_type)
|
| 2104 |
|
| 2105 | # Copy of visit_block, without opening {
|
| 2106 | self.indent += 1
|
| 2107 | block = o.body
|
| 2108 | self._WriteBody(block.body)
|
| 2109 | self.indent -= 1
|
| 2110 | self.write_ind('}\n')
|
| 2111 |
|
| 2112 | if o.else_body:
|
| 2113 | raise AssertionError("can't translate for-else")
|
| 2114 |
|
| 2115 | def _WriteCases(self, switch_expr: Expression, cases: util.CaseList,
|
| 2116 | default_block: Union['mypy.nodes.Block', int]) -> None:
|
| 2117 | """ Write a list of (expr, block) pairs """
|
| 2118 |
|
| 2119 | for expr, body in cases:
|
| 2120 | assert expr is not None, expr
|
| 2121 | if not isinstance(expr, CallExpr):
|
| 2122 | self.report_error(expr,
|
| 2123 | 'Expected call like case(x), got %s' % expr)
|
| 2124 | return
|
| 2125 |
|
| 2126 | for i, arg in enumerate(expr.args):
|
| 2127 | if i != 0:
|
| 2128 | self.write('\n')
|
| 2129 | self.write_ind('case ')
|
| 2130 | self.accept(arg)
|
| 2131 | self.write(': ')
|
| 2132 |
|
| 2133 | self.accept(body)
|
| 2134 | self.write_ind(' break;\n')
|
| 2135 |
|
| 2136 | if default_block == -1:
|
| 2137 | # an error occurred
|
| 2138 | return
|
| 2139 | if default_block == -2:
|
| 2140 | # This is too restrictive
|
| 2141 | #self.report_error(switch_expr,
|
| 2142 | # 'switch got no else: for default block')
|
| 2143 | return
|
| 2144 |
|
| 2145 | # Narrow the type
|
| 2146 | assert not isinstance(default_block, int), default_block
|
| 2147 |
|
| 2148 | self.write_ind('default: ')
|
| 2149 | self.accept(default_block)
|
| 2150 | # don't write 'break'
|
| 2151 |
|
| 2152 | def _WriteSwitch(self, expr: CallExpr, o: 'mypy.nodes.WithStmt') -> None:
|
| 2153 | """Write a switch statement over integers."""
|
| 2154 | assert len(expr.args) == 1, expr.args
|
| 2155 |
|
| 2156 | self.write_ind('switch (')
|
| 2157 | self.accept(expr.args[0])
|
| 2158 | self.write(') {\n')
|
| 2159 |
|
| 2160 | assert len(o.body.body) == 1, o.body.body
|
| 2161 | if_node = o.body.body[0]
|
| 2162 | assert isinstance(if_node, IfStmt), if_node
|
| 2163 |
|
| 2164 | self.indent += 1
|
| 2165 | cases: util.CaseList = []
|
| 2166 | default_block = util.CollectSwitchCases(self.module_path,
|
| 2167 | if_node,
|
| 2168 | cases,
|
| 2169 | errors=self.errors_keep_going)
|
| 2170 | self._WriteCases(expr, cases, default_block)
|
| 2171 |
|
| 2172 | self.indent -= 1
|
| 2173 | self.write_ind('}\n')
|
| 2174 |
|
| 2175 | def _WriteTagSwitch(self, expr: CallExpr,
|
| 2176 | o: 'mypy.nodes.WithStmt') -> None:
|
| 2177 | """Write a switch statement over ASDL types."""
|
| 2178 | assert len(expr.args) == 1, expr.args
|
| 2179 |
|
| 2180 | self.write_ind('switch (')
|
| 2181 | self.accept(expr.args[0])
|
| 2182 | self.write('->tag()) {\n')
|
| 2183 |
|
| 2184 | assert len(o.body.body) == 1, o.body.body
|
| 2185 | if_node = o.body.body[0]
|
| 2186 | assert isinstance(if_node, IfStmt), if_node
|
| 2187 |
|
| 2188 | self.indent += 1
|
| 2189 | cases: util.CaseList = []
|
| 2190 | default_block = util.CollectSwitchCases(self.module_path,
|
| 2191 | if_node,
|
| 2192 | cases,
|
| 2193 | errors=self.errors_keep_going)
|
| 2194 | self._WriteCases(expr, cases, default_block)
|
| 2195 |
|
| 2196 | self.indent -= 1
|
| 2197 | self.write_ind('}\n')
|
| 2198 |
|
| 2199 | def _StrSwitchCases(self, cases: util.CaseList) -> Any:
|
| 2200 | cases2: List[Tuple[int, str, 'mypy.nodes.Block']] = []
|
| 2201 | for expr, body in cases:
|
| 2202 | if not isinstance(expr, CallExpr):
|
| 2203 | # non-fatal check from CollectSwitchCases
|
| 2204 | break
|
| 2205 |
|
| 2206 | args = expr.args
|
| 2207 | if len(args) != 1:
|
| 2208 | self.report_error(
|
| 2209 | expr,
|
| 2210 | 'str_switch can only have case("x"), not case("x", "y"): got %r'
|
| 2211 | % args)
|
| 2212 | break
|
| 2213 |
|
| 2214 | if not isinstance(args[0], StrExpr):
|
| 2215 | self.report_error(
|
| 2216 | expr,
|
| 2217 | 'str_switch can only be used with constant strings, got %s'
|
| 2218 | % args[0])
|
| 2219 | break
|
| 2220 |
|
| 2221 | s = args[0].value
|
| 2222 | cases2.append((len(s), s, body))
|
| 2223 |
|
| 2224 | # Sort by string length
|
| 2225 | cases2.sort(key=lambda pair: pair[0])
|
| 2226 | grouped = itertools.groupby(cases2, key=lambda pair: pair[0])
|
| 2227 | return grouped
|
| 2228 |
|
| 2229 | def _WriteStrSwitch(self, expr: CallExpr,
|
| 2230 | o: 'mypy.nodes.WithStmt') -> None:
|
| 2231 | """Write a switch statement over strings."""
|
| 2232 | assert len(expr.args) == 1, expr.args
|
| 2233 |
|
| 2234 | switch_expr = expr # for later error
|
| 2235 |
|
| 2236 | switch_var = expr.args[0]
|
| 2237 | if not isinstance(switch_var, NameExpr):
|
| 2238 | self.report_error(
|
| 2239 | expr.args[0],
|
| 2240 | 'str_switch(x) accepts only a variable name, got %s' %
|
| 2241 | switch_var)
|
| 2242 | return
|
| 2243 |
|
| 2244 | self.write_ind('switch (len(%s)) {\n' % switch_var.name)
|
| 2245 |
|
| 2246 | # There can only be one thing under 'with str_switch'
|
| 2247 | assert len(o.body.body) == 1, o.body.body
|
| 2248 | if_node = o.body.body[0]
|
| 2249 | assert isinstance(if_node, IfStmt), if_node
|
| 2250 |
|
| 2251 | self.indent += 1
|
| 2252 |
|
| 2253 | cases: util.CaseList = []
|
| 2254 | default_block = util.CollectSwitchCases(self.module_path,
|
| 2255 | if_node,
|
| 2256 | cases,
|
| 2257 | errors=self.errors_keep_going)
|
| 2258 |
|
| 2259 | grouped_cases = self._StrSwitchCases(cases)
|
| 2260 | # Warning: this consumes internal iterator
|
| 2261 | #self.log('grouped %s', list(grouped_cases))
|
| 2262 |
|
| 2263 | for str_len, group in grouped_cases:
|
| 2264 | self.write_ind('case %s: {\n' % str_len)
|
| 2265 | if_num = 0
|
| 2266 | for _, case_str, block in group:
|
| 2267 | self.indent += 1
|
| 2268 |
|
| 2269 | else_str = '' if if_num == 0 else 'else '
|
| 2270 | self.write_ind('%sif (str_equals_c(%s, %s, %d)) ' %
|
| 2271 | (else_str, switch_var.name,
|
| 2272 | PythonStringLiteral(case_str), str_len))
|
| 2273 | self.accept(block)
|
| 2274 |
|
| 2275 | self.indent -= 1
|
| 2276 | if_num += 1
|
| 2277 |
|
| 2278 | self.indent += 1
|
| 2279 | self.write_ind('else {\n')
|
| 2280 | self.write_ind(' goto str_switch_default;\n')
|
| 2281 | self.write_ind('}\n')
|
| 2282 | self.indent -= 1
|
| 2283 |
|
| 2284 | self.write_ind('}\n')
|
| 2285 | self.write_ind(' break;\n')
|
| 2286 |
|
| 2287 | if default_block == -1:
|
| 2288 | # an error occurred
|
| 2289 | return
|
| 2290 | if default_block == -2:
|
| 2291 | self.report_error(switch_expr,
|
| 2292 | 'str_switch got no else: for default block')
|
| 2293 | return
|
| 2294 |
|
| 2295 | # Narrow the type
|
| 2296 | assert not isinstance(default_block, int), default_block
|
| 2297 |
|
| 2298 | self.write('\n')
|
| 2299 | self.write_ind('str_switch_default:\n')
|
| 2300 | self.write_ind('default: ')
|
| 2301 | self.accept(default_block)
|
| 2302 |
|
| 2303 | self.indent -= 1
|
| 2304 | self.write_ind('}\n')
|
| 2305 |
|
| 2306 | def visit_with_stmt(self, o: 'mypy.nodes.WithStmt') -> None:
|
| 2307 | """
|
| 2308 | Translate only blocks of this form:
|
| 2309 |
|
| 2310 | with switch(x) as case:
|
| 2311 | if case(0):
|
| 2312 | print('zero')
|
| 2313 | elif case(1, 2, 3):
|
| 2314 | print('low')
|
| 2315 | else:
|
| 2316 | print('other')
|
| 2317 |
|
| 2318 | switch(x) {
|
| 2319 | case 0:
|
| 2320 | print('zero')
|
| 2321 | break;
|
| 2322 | case 1:
|
| 2323 | case 2:
|
| 2324 | case 3:
|
| 2325 | print('low')
|
| 2326 | break;
|
| 2327 | default:
|
| 2328 | print('other')
|
| 2329 | break;
|
| 2330 | }
|
| 2331 |
|
| 2332 | Or:
|
| 2333 |
|
| 2334 | with ctx_Bar(bar, x, y):
|
| 2335 | x()
|
| 2336 |
|
| 2337 | {
|
| 2338 | ctx_Bar(bar, x, y)
|
| 2339 | x();
|
| 2340 | }
|
| 2341 | """
|
| 2342 | #log('WITH')
|
| 2343 | #log('expr %s', o.expr)
|
| 2344 | #log('target %s', o.target)
|
| 2345 |
|
| 2346 | assert len(o.expr) == 1, o.expr
|
| 2347 | expr = o.expr[0]
|
| 2348 | assert isinstance(expr, CallExpr), expr
|
| 2349 |
|
| 2350 | # There is no 'with mylib.tagswitch(x)', only 'with tagswitch(x)'
|
| 2351 | # But we have with alloc.ctx_SourceCode
|
| 2352 | #assert isinstance(expr.callee, NameExpr), expr.callee
|
| 2353 |
|
| 2354 | callee_name = expr.callee.name
|
| 2355 | if callee_name == 'switch':
|
| 2356 | self._WriteSwitch(expr, o)
|
| 2357 | elif callee_name == 'str_switch':
|
| 2358 | self._WriteStrSwitch(expr, o)
|
| 2359 | elif callee_name == 'tagswitch':
|
| 2360 | self._WriteTagSwitch(expr, o)
|
| 2361 | else:
|
| 2362 | assert isinstance(expr, CallExpr), expr
|
| 2363 | self.write_ind('{ // with\n')
|
| 2364 | self.indent += 1
|
| 2365 |
|
| 2366 | self.write_ind('')
|
| 2367 | self.accept(expr.callee)
|
| 2368 |
|
| 2369 | # FIX: Use braced initialization to avoid most-vexing parse when
|
| 2370 | # there are 0 args!
|
| 2371 | self.write(' ctx{')
|
| 2372 | for i, arg in enumerate(expr.args):
|
| 2373 | if i != 0:
|
| 2374 | self.write(', ')
|
| 2375 | self.accept(arg)
|
| 2376 | self.write('};\n\n')
|
| 2377 |
|
| 2378 | self._WriteBody(o.body.body)
|
| 2379 |
|
| 2380 | self.indent -= 1
|
| 2381 | self.write_ind('}\n')
|
| 2382 |
|
| 2383 | def visit_del_stmt(self, o: 'mypy.nodes.DelStmt') -> None:
|
| 2384 |
|
| 2385 | d = o.expr
|
| 2386 | if isinstance(d, IndexExpr):
|
| 2387 | self.write_ind('')
|
| 2388 | self.accept(d.base)
|
| 2389 |
|
| 2390 | if isinstance(d.index, SliceExpr):
|
| 2391 | # del mylist[:] -> mylist->clear()
|
| 2392 |
|
| 2393 | sl = d.index
|
| 2394 | assert sl.begin_index is None, sl
|
| 2395 | assert sl.end_index is None, sl
|
| 2396 | self.write('->clear()')
|
| 2397 | else:
|
| 2398 | # del mydict[mykey] raises KeyError, which we don't want
|
| 2399 | raise AssertionError(
|
| 2400 | 'Use mylib.dict_erase(d, key) instead of del d[key]')
|
| 2401 |
|
| 2402 | self.write(';\n')
|
| 2403 |
|
| 2404 | def oils_visit_constructor(self, o: ClassDef, stmt: FuncDef,
|
| 2405 | base_class_sym: util.SymbolPath) -> None:
|
| 2406 | self.write('\n')
|
| 2407 | self.write('%s::%s(', o.name, o.name)
|
| 2408 | self._WriteFuncParams(stmt, write_defaults=False)
|
| 2409 | self.write(')')
|
| 2410 |
|
| 2411 | first_index = 0
|
| 2412 |
|
| 2413 | # Skip docstring
|
| 2414 | maybe_skip_stmt = stmt.body.body[0]
|
| 2415 | if (isinstance(maybe_skip_stmt, ExpressionStmt) and
|
| 2416 | isinstance(maybe_skip_stmt.expr, StrExpr)):
|
| 2417 | first_index += 1
|
| 2418 |
|
| 2419 | # Check for Base.__init__(self, ...) and move that to the initializer list.
|
| 2420 | first_stmt = stmt.body.body[first_index]
|
| 2421 | if (isinstance(first_stmt, ExpressionStmt) and
|
| 2422 | isinstance(first_stmt.expr, CallExpr)):
|
| 2423 | expr = first_stmt.expr
|
| 2424 | #log('expr %s', expr)
|
| 2425 | callee = first_stmt.expr.callee
|
| 2426 |
|
| 2427 | # TextOutput() : ColorOutput(f), ... {
|
| 2428 | if (isinstance(callee, MemberExpr) and callee.name == '__init__'):
|
| 2429 | base_constructor_args = expr.args
|
| 2430 | #log('ARGS %s', base_constructor_args)
|
| 2431 | self.write(' : %s(',
|
| 2432 | SymbolToString(base_class_sym, strip_package=True))
|
| 2433 | for i, arg in enumerate(base_constructor_args):
|
| 2434 | if i == 0:
|
| 2435 | continue # Skip 'this'
|
| 2436 | if i != 1:
|
| 2437 | self.write(', ')
|
| 2438 | self.accept(arg)
|
| 2439 | self.write(')')
|
| 2440 |
|
| 2441 | first_index += 1
|
| 2442 |
|
| 2443 | self.write(' {\n')
|
| 2444 |
|
| 2445 | # Now visit the rest of the statements
|
| 2446 | self.indent += 1
|
| 2447 |
|
| 2448 | if _IsContextManager(self.current_class_name):
|
| 2449 | # For ctx_* classes only, do gHeap.PushRoot() for all the pointer
|
| 2450 | # members
|
| 2451 | member_vars = self.all_member_vars[o]
|
| 2452 | for name in sorted(member_vars):
|
| 2453 | _, c_type, is_managed = member_vars[name]
|
| 2454 | if is_managed:
|
| 2455 | # VALIDATE_ROOTS doesn't complain even if it's not
|
| 2456 | # initialized? Should be initialized after PushRoot().
|
| 2457 | #self.write_ind('this->%s = nullptr;\n' % name)
|
| 2458 | self.write_ind(
|
| 2459 | 'gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->%s)));\n'
|
| 2460 | % name)
|
| 2461 |
|
| 2462 | for node in stmt.body.body[first_index:]:
|
| 2463 | self.accept(node)
|
| 2464 | self.indent -= 1
|
| 2465 | self.write('}\n')
|
| 2466 |
|
| 2467 | def oils_visit_dunder_exit(self, o: ClassDef, stmt: FuncDef,
|
| 2468 | base_class_sym: util.SymbolPath) -> None:
|
| 2469 | self.write('\n')
|
| 2470 | self.write_ind('%s::~%s()', o.name, o.name)
|
| 2471 |
|
| 2472 | self.write(' {\n')
|
| 2473 | self.indent += 1
|
| 2474 |
|
| 2475 | # TODO:
|
| 2476 | # - Can't throw exception in destructor.
|
| 2477 | # - Check that you don't return early from destructor. If so, we skip
|
| 2478 | # PopRoot(), which messes up the invariant!
|
| 2479 |
|
| 2480 | for node in stmt.body.body:
|
| 2481 | self.accept(node)
|
| 2482 |
|
| 2483 | # For ctx_* classes only , gHeap.PopRoot() for all the pointer members
|
| 2484 | if _IsContextManager(self.current_class_name):
|
| 2485 | member_vars = self.all_member_vars[o]
|
| 2486 | for name in sorted(member_vars):
|
| 2487 | _, c_type, is_managed = member_vars[name]
|
| 2488 | if is_managed:
|
| 2489 | self.write_ind('gHeap.PopRoot();\n')
|
| 2490 | else:
|
| 2491 | self.report_error(
|
| 2492 | o, 'Any class with __exit__ should be named ctx_Foo (%s)' %
|
| 2493 | (self.current_class_name, ))
|
| 2494 | return
|
| 2495 |
|
| 2496 | self.indent -= 1
|
| 2497 | self.write('}\n')
|
| 2498 |
|
| 2499 | def oils_visit_method(self, o: ClassDef, stmt: FuncDef,
|
| 2500 | base_class_sym: util.SymbolPath) -> None:
|
| 2501 | self.accept(stmt)
|
| 2502 |
|
| 2503 | # Module structure
|
| 2504 |
|
| 2505 | def visit_import(self, o: 'mypy.nodes.Import') -> None:
|
| 2506 | pass
|
| 2507 |
|
| 2508 | def visit_import_from(self, o: 'mypy.nodes.ImportFrom') -> None:
|
| 2509 | """
|
| 2510 | Write C++ namespace aliases and 'using' for imports.
|
| 2511 | We need them in the 'decl' phase for default arguments like
|
| 2512 | runtime_asdl::scope_e -> scope_e
|
| 2513 | """
|
| 2514 | if o.id in ('__future__', 'typing'):
|
| 2515 | return # do nothing
|
| 2516 |
|
| 2517 | for name, alias in o.names:
|
| 2518 | #self.log('ImportFrom id: %s name: %s alias: %s', o.id, name, alias)
|
| 2519 |
|
| 2520 | if name == 'log': # varargs translation
|
| 2521 | continue
|
| 2522 |
|
| 2523 | if o.id == 'mycpp.mylib':
|
| 2524 | # These mylib functions are translated in a special way
|
| 2525 | if name in ('switch', 'tagswitch', 'str_switch', 'iteritems',
|
| 2526 | 'NewDict', 'probe'):
|
| 2527 | continue
|
| 2528 | # STDIN_FILENO is #included
|
| 2529 | if name == 'STDIN_FILENO':
|
| 2530 | continue
|
| 2531 |
|
| 2532 | # A heuristic that works for the Oils import style.
|
| 2533 | if '.' in o.id:
|
| 2534 | # from mycpp.mylib import log => using mylib::log
|
| 2535 | translate_import = True
|
| 2536 | else:
|
| 2537 | # from core import util => NOT translated
|
| 2538 | # We just rely on 'util' being defined.
|
| 2539 | translate_import = False
|
| 2540 |
|
| 2541 | if translate_import:
|
| 2542 | dotted_parts = o.id.split('.')
|
| 2543 | last_dotted = dotted_parts[-1]
|
| 2544 |
|
| 2545 | # Omit these:
|
| 2546 | # from _gen.ysh import grammar_nt
|
| 2547 | if last_dotted == 'ysh':
|
| 2548 | return
|
| 2549 | # from _devbuild.gen import syntax_asdl
|
| 2550 | if last_dotted == 'gen':
|
| 2551 | return
|
| 2552 |
|
| 2553 | # Problem:
|
| 2554 | # - The decl stage has to return yaks_asdl::mod_def, so imports should go there
|
| 2555 | # - But if you change this to decl_write() instead of
|
| 2556 | # write(), you end up 'using error::e_usage' in say
|
| 2557 | # 'assign_osh', and it hasn't been defined yet.
|
| 2558 |
|
| 2559 | if alias:
|
| 2560 | # using runtime_asdl::emit_e = EMIT;
|
| 2561 | self.write_ind('using %s = %s::%s;\n', alias, last_dotted,
|
| 2562 | name)
|
| 2563 | else:
|
| 2564 | # from _devbuild.gen.id_kind_asdl import Id
|
| 2565 | # -> using id_kind_asdl::Id.
|
| 2566 | using_str = 'using %s::%s;\n' % (last_dotted, name)
|
| 2567 | self.write_ind(using_str)
|
| 2568 |
|
| 2569 | # Fully qualified:
|
| 2570 | # self.write_ind('using %s::%s;\n', '::'.join(dotted_parts), name)
|
| 2571 |
|
| 2572 | else:
|
| 2573 | # If we're importing a module without an alias, we don't need to do
|
| 2574 | # anything. 'namespace cmd_eval' is already defined.
|
| 2575 | if not alias:
|
| 2576 | return
|
| 2577 |
|
| 2578 | # from asdl import format as fmt
|
| 2579 | # -> namespace fmt = format;
|
| 2580 | self.write_ind('namespace %s = %s;\n', alias, name)
|
| 2581 |
|
| 2582 | # Statements
|
| 2583 |
|
| 2584 | def _WriteLocals(self, local_var_list: List[LocalVar]) -> None:
|
| 2585 | # TODO: put the pointers first, and then register a single
|
| 2586 | # StackRoots record.
|
| 2587 | done = set()
|
| 2588 | for lval_name, lval_type, is_param in local_var_list:
|
| 2589 | c_type = GetCType(lval_type)
|
| 2590 | if not is_param and lval_name not in done:
|
| 2591 | if util.SMALL_STR and c_type == 'Str':
|
| 2592 | self.write_ind('%s %s(nullptr);\n', c_type, lval_name)
|
| 2593 | else:
|
| 2594 | rhs = ' = nullptr' if CTypeIsManaged(c_type) else ''
|
| 2595 | self.write_ind('%s %s%s;\n', c_type, lval_name, rhs)
|
| 2596 |
|
| 2597 | # TODO: we're not skipping the assignment, because of
|
| 2598 | # the RHS
|
| 2599 | if util.IsUnusedVar(lval_name):
|
| 2600 | # suppress C++ unused var compiler warnings!
|
| 2601 | self.write_ind('(void)%s;\n' % lval_name)
|
| 2602 |
|
| 2603 | done.add(lval_name)
|
| 2604 |
|
| 2605 | # Figure out if we have any roots to write with StackRoots
|
| 2606 | roots = [] # keep it sorted
|
| 2607 | full_func_name = None
|
| 2608 | if self.current_func_node:
|
| 2609 | full_func_name = SplitPyName(self.current_func_node.fullname)
|
| 2610 |
|
| 2611 | for lval_name, lval_type, is_param in local_var_list:
|
| 2612 | c_type = GetCType(lval_type)
|
| 2613 | #self.log('%s %s %s', lval_name, c_type, is_param)
|
| 2614 | if lval_name not in roots and CTypeIsManaged(c_type):
|
| 2615 | if (not self.stack_roots or self.stack_roots.needs_root(
|
| 2616 | full_func_name, SplitPyName(lval_name))):
|
| 2617 | roots.append(lval_name)
|
| 2618 |
|
| 2619 | #self.log('roots %s', roots)
|
| 2620 |
|
| 2621 | if len(roots):
|
| 2622 | if (self.stack_roots_warn and len(roots) > self.stack_roots_warn):
|
| 2623 | log('WARNING: %s() has %d stack roots. Consider refactoring this function.'
|
| 2624 | % (self.current_func_node.fullname, len(roots)))
|
| 2625 |
|
| 2626 | for i, r in enumerate(roots):
|
| 2627 | self.write_ind('StackRoot _root%d(&%s);\n' % (i, r))
|
| 2628 |
|
| 2629 | self.write('\n')
|
| 2630 |
|
| 2631 | def visit_block(self, block: 'mypy.nodes.Block') -> None:
|
| 2632 | self.write('{\n') # not indented to use same line as while/if
|
| 2633 |
|
| 2634 | self.indent += 1
|
| 2635 | self._WriteBody(block.body)
|
| 2636 | self.indent -= 1
|
| 2637 |
|
| 2638 | self.write_ind('}\n')
|
| 2639 |
|
| 2640 | def oils_visit_expression_stmt(self,
|
| 2641 | o: 'mypy.nodes.ExpressionStmt') -> None:
|
| 2642 | self.write_ind('')
|
| 2643 | self.accept(o.expr)
|
| 2644 | self.write(';\n')
|
| 2645 |
|
| 2646 | def visit_operator_assignment_stmt(
|
| 2647 | self, o: 'mypy.nodes.OperatorAssignmentStmt') -> None:
|
| 2648 | self.write_ind('')
|
| 2649 | self.accept(o.lvalue)
|
| 2650 | self.write(' %s= ', o.op) # + to +=
|
| 2651 | self.accept(o.rvalue)
|
| 2652 | self.write(';\n')
|
| 2653 |
|
| 2654 | def visit_while_stmt(self, o: 'mypy.nodes.WhileStmt') -> None:
|
| 2655 | self.write_ind('while (')
|
| 2656 | self.accept(o.expr)
|
| 2657 | self.write(') ')
|
| 2658 | self.accept(o.body)
|
| 2659 |
|
| 2660 | def visit_return_stmt(self, o: 'mypy.nodes.ReturnStmt') -> None:
|
| 2661 | # Examples:
|
| 2662 | # return
|
| 2663 | # return None
|
| 2664 | # return my_int + 3;
|
| 2665 | self.write_ind('return ')
|
| 2666 | if o.expr:
|
| 2667 | if not (isinstance(o.expr, NameExpr) and o.expr.name == 'None'):
|
| 2668 |
|
| 2669 | # Note: the type of the return expression (self.types[o.expr])
|
| 2670 | # and the return type of the FUNCTION are different. Use the
|
| 2671 | # latter.
|
| 2672 | ret_type = self.current_func_node.type.ret_type
|
| 2673 |
|
| 2674 | c_ret_type, returning_tuple, _ = GetCReturnType(ret_type)
|
| 2675 |
|
| 2676 | # return '', None # tuple literal
|
| 2677 | # but NOT
|
| 2678 | # return tuple_func()
|
| 2679 | if returning_tuple and isinstance(o.expr, TupleExpr):
|
| 2680 | self.write('%s(' % c_ret_type)
|
| 2681 | for i, item in enumerate(o.expr.items):
|
| 2682 | if i != 0:
|
| 2683 | self.write(', ')
|
| 2684 | self.accept(item)
|
| 2685 | self.write(');\n')
|
| 2686 | return
|
| 2687 |
|
| 2688 | # Not returning tuple
|
| 2689 | self.accept(o.expr)
|
| 2690 |
|
| 2691 | self.write(';\n')
|
| 2692 |
|
| 2693 | def visit_if_stmt(self, o: 'mypy.nodes.IfStmt') -> None:
|
| 2694 | # Not sure why this wouldn't be true
|
| 2695 | assert len(o.expr) == 1, o.expr
|
| 2696 |
|
| 2697 | condition = o.expr[0]
|
| 2698 |
|
| 2699 | if not _CheckCondition(condition, self.types):
|
| 2700 | self.report_error(
|
| 2701 | o,
|
| 2702 | "Use explicit len(obj) or 'obj is not None' for mystr, mylist, mydict"
|
| 2703 | )
|
| 2704 | return
|
| 2705 |
|
| 2706 | if util.ShouldVisitIfExpr(o):
|
| 2707 | self.write_ind('if (')
|
| 2708 | for e in o.expr:
|
| 2709 | self.accept(e)
|
| 2710 | self.write(') ')
|
| 2711 |
|
| 2712 | if util.ShouldVisitIfBody(o):
|
| 2713 | cond = util.GetSpecialIfCondition(o)
|
| 2714 | if cond == 'CPP':
|
| 2715 | self.write_ind('// if MYCPP\n')
|
| 2716 | self.write_ind('')
|
| 2717 |
|
| 2718 | for body in o.body:
|
| 2719 | self.accept(body)
|
| 2720 |
|
| 2721 | if cond == 'CPP':
|
| 2722 | self.write_ind('// endif MYCPP\n')
|
| 2723 |
|
| 2724 | if util.ShouldVisitElseBody(o):
|
| 2725 | cond = util.GetSpecialIfCondition(o)
|
| 2726 | if cond == 'PYTHON':
|
| 2727 | self.write_ind('// if not PYTHON\n')
|
| 2728 | self.write_ind('')
|
| 2729 |
|
| 2730 | if util.ShouldVisitIfBody(o):
|
| 2731 | self.write_ind('else ')
|
| 2732 |
|
| 2733 | self.accept(o.else_body)
|
| 2734 |
|
| 2735 | if cond == 'PYTHON':
|
| 2736 | self.write_ind('// endif MYCPP\n')
|
| 2737 |
|
| 2738 | def visit_break_stmt(self, o: 'mypy.nodes.BreakStmt') -> None:
|
| 2739 | self.write_ind('break;\n')
|
| 2740 |
|
| 2741 | def visit_continue_stmt(self, o: 'mypy.nodes.ContinueStmt') -> None:
|
| 2742 | self.write_ind('continue;\n')
|
| 2743 |
|
| 2744 | def visit_pass_stmt(self, o: 'mypy.nodes.PassStmt') -> None:
|
| 2745 | self.write_ind('; // pass\n')
|
| 2746 |
|
| 2747 | def visit_raise_stmt(self, o: 'mypy.nodes.RaiseStmt') -> None:
|
| 2748 | to_raise = o.expr
|
| 2749 |
|
| 2750 | if to_raise:
|
| 2751 | if isinstance(to_raise, CallExpr) and isinstance(
|
| 2752 | to_raise.callee, NameExpr):
|
| 2753 | callee_name = to_raise.callee.name
|
| 2754 | if callee_name == 'AssertionError':
|
| 2755 | # C++ compiler is aware of assert(0) for unreachable code
|
| 2756 | self.write_ind('assert(0); // AssertionError\n')
|
| 2757 | return
|
| 2758 | if callee_name == 'NotImplementedError':
|
| 2759 | self.write_ind(
|
| 2760 | 'FAIL(kNotImplemented); // Python NotImplementedError\n'
|
| 2761 | )
|
| 2762 | return
|
| 2763 | self.write_ind('throw ')
|
| 2764 | self.accept(to_raise)
|
| 2765 | self.write(';\n')
|
| 2766 | else:
|
| 2767 | # raise without arg
|
| 2768 | self.write_ind('throw;\n')
|
| 2769 |
|
| 2770 | def visit_try_stmt(self, o: 'mypy.nodes.TryStmt') -> None:
|
| 2771 | self.write_ind('try ')
|
| 2772 | self.accept(o.body)
|
| 2773 | caught = False
|
| 2774 |
|
| 2775 | for t, v, handler in zip(o.types, o.vars, o.handlers):
|
| 2776 | c_type = None
|
| 2777 |
|
| 2778 | if isinstance(t, NameExpr):
|
| 2779 | if t.name in ('IOError', 'OSError'):
|
| 2780 | self.report_error(
|
| 2781 | handler,
|
| 2782 | 'Use except (IOError, OSError) rather than catching just one'
|
| 2783 | )
|
| 2784 | c_type = '%s*' % t.name
|
| 2785 |
|
| 2786 | elif isinstance(t, MemberExpr):
|
| 2787 | # We never use 'except foo.bar.T', only `foo.T'
|
| 2788 | assert isinstance(t.expr, NameExpr), t.expr
|
| 2789 | c_type = '%s::%s*' % (t.expr.name, t.name)
|
| 2790 |
|
| 2791 | elif isinstance(t, TupleExpr):
|
| 2792 | if len(t.items) == 2:
|
| 2793 | e1 = t.items[0]
|
| 2794 | e2 = t.items[1]
|
| 2795 | if isinstance(e1, NameExpr) and isinstance(e2, NameExpr):
|
| 2796 | names = [e1.name, e2.name]
|
| 2797 | names.sort()
|
| 2798 | if names == ['IOError', 'OSError']:
|
| 2799 | c_type = 'IOError_OSError*' # Base class in mylib
|
| 2800 |
|
| 2801 | else:
|
| 2802 | raise AssertionError()
|
| 2803 |
|
| 2804 | if c_type is None:
|
| 2805 | self.report_error(o, "try couldn't determine c_type")
|
| 2806 | return
|
| 2807 |
|
| 2808 | if v:
|
| 2809 | self.write_ind('catch (%s %s) ', c_type, v.name)
|
| 2810 | else:
|
| 2811 | self.write_ind('catch (%s) ', c_type)
|
| 2812 | self.accept(handler)
|
| 2813 |
|
| 2814 | caught = True
|
| 2815 |
|
| 2816 | if not caught:
|
| 2817 | self.report_error(o, 'try should have an except')
|
| 2818 |
|
| 2819 | if o.else_body:
|
| 2820 | self.report_error(o, 'try/else not supported')
|
| 2821 |
|
| 2822 | if o.finally_body:
|
| 2823 | self.report_error(o, 'try/finally not supported')
|