def visit_For(self, node: ast.For) -> None: # create list to iterate over lst_name = self.context.get_temp_name() assign_node = ast.Assign( targets=[ast.Name(id=lst_name, ctx=ast.Store())], value=node.iter) self.visit(assign_node) lst = self.context[lst_name] index = self.context.get_temp_var(TypeDB.get_type_by_name("int")) length = lst.tp.get_method("len") length_code = length.get_code(self.context, lst).code # construct for statement self.start_line( f"for({index.code}=0; {index.code} < {length_code}; {index.code}++) {{\n" ) self.indent += 4 assign_node = ast.Assign( targets=[node.target], value=ast.Subscript( value=ast.Name(id=lst_name, ctx=ast.Load()), slice=ast.Index(value=ast.Name(id=index.code, ctx=ast.Load())), ctx=ast.Load())) self.visit(assign_node) for statement in node.body: self.visit(statement) self.indent -= 4 self.start_line("}\n") self.all_paths_return = False
def visit_Decl(self, node) -> t.Union[typed_ast3.AnnAssign, # pylint: disable=invalid-name t.Tuple[str, typed_ast3.arguments, typed_ast3.AST]]: """Transform Decl.""" name = node.name assert isinstance(name, str), type(name) quals = node.quals if quals: _LOG.warning('ignoring unsupported C grammar: %s', quals) storage = [self.visit(subnode) for subnode in node.storage] if storage: raise NotImplementedError(_node_debug(node.storage), str(storage)) funcspec = [self.visit(subnode) for subnode in node.funcspec] if funcspec: raise NotImplementedError(_node_debug(node.funcspec), str(funcspec)) type_data = self.visit(node.type) assert isinstance(type_data, tuple) assert len(type_data) == DECL_DATA_LENGTHS[type(node.type)], (type(node.type), type_data) init = self.visit(node.init) if init is not None: assert isinstance(node.type, INITIALIZABLE_DECLARATIONS) # assert isinstance(node.type, c_ast.TypeDecl), type(node.type) # raise NotImplementedError(_node_debug(node.init), str(init)) bitsize = self.visit(node.bitsize) if bitsize is not None: raise NotImplementedError(_node_debug(node.bitsize), str(bitsize)) _ = self.visit(node.coord) if init is not None or isinstance(node.type, INITIALIZABLE_DECLARATIONS): name_, type_ = type_data assert name_ == name return typed_ast3.AnnAssign(target=typed_ast3.Name(id=name_, ctx=typed_ast3.Store()), annotation=type_, value=init, simple=1) if isinstance(node.type, (c_ast.FuncDecl,)): return type_data return self.generic_visit(node)
def test_non_str_type_comment(self): examples = { typed_ast3.Assign(targets=[ typed_ast3.Name('x', typed_ast3.Store()) ], value=typed_ast3.Str('universe, life, and everything'), type_comment=typed_ast3.Str('42')): logging.DEBUG, typed_ast3.Assign(targets=[ typed_ast3.Name('x', typed_ast3.Store()) ], value=typed_ast3.Str('universe, life, and everything'), type_comment=42): logging.WARNING } for example, expected_level in examples.items(): resolver = TypeHintResolver[typed_ast3, typed_ast3](eval_=False) with self.subTest(example=example, expected_level=expected_level): with self.assertLogs(level=expected_level): resolver.visit(example)
def visit_Typedef(self, node): # pylint: disable=invalid-name """Transform Typedef.""" name = node.name assert isinstance(name, str), type(name) quals = node.quals if quals: _LOG.warning('ignoring unsupported C grammar: %s', quals) assert node.storage == ['typedef'], node.storage name_, type_ = self.visit(node.type) assert name == name_, (name, name_) _ = self.visit(node.coord) return typed_ast3.AnnAssign(target=typed_ast3.Name(name, typed_ast3.Store()), value=type_, annotation=typed_ast3.Name('type', typed_ast3.Load()), simple=1)
def visit_Attribute(self, node: ast3.Attribute): # If this Attribute isn't being used on the left-hand side of an assignment # statement, the visitor doesn't need to save it. if not self._is_store(node): return if self._in_class: # If in a class, only save "self.attr" expressions. if isinstance(node.value, ast3.Name) and node.value.id == "self": self._namespace[node.attr].append( definitions.Variable(name=node.attr, source=self._source, lineno=node.lineno, col_offset=node.col_offset)) else: # Outside of a class, an expression like "a.b.c" is stored as # Variable(name = "a"). When parsed, "a.b.c" becomes # Attribute(Attribute(Name("a"), "b"), "c"). Since only "a" needs to be # saved, proceed with the visit as normal so visit_Name handles "a". # However, "a" and "b" are considered Loads, not Stores, so we need to # update the node. node.value.ctx = ast3.Store() self.generic_visit(node)
def visit_ImportFrom(self, node: ast3.ImportFrom) -> VisitorOutput: """Defines how to import (from) modules (supported and nonsupported) For example, it converts:: from numpy import array from numpy import * from somelib import var, othervar as var2 from otherlib import * into:: from pytropos.libs_checking import numpy_module st['array'] = numpy_module.attr['array', pos...] from pytropos.libs_checking import numpy_module st.importStar(numpy_module) st['var'] = pt.Top st['var2'] = pt.Top st.importStar() """ libs: 'List[ast3.AST]' = [] if node.module in self._supported_modules: module_name = self._supported_modules[node.module] # from pytropos.libs_checking import module_name libs.append( ast3.ImportFrom( module='pytropos.libs_checking', names=[ast3.alias(name=module_name, asname=None)], level=0, )) if node.names[0].name == '*': # st.importStar(module_name) libs.append( ast3.Expr(value=ast3.Call( func=ast3.Attribute( value=ast3.Name(id='st', ctx=ast3.Load()), attr='importStar', ctx=ast3.Load(), ), args=[ast3.Name(id=module_name, ctx=ast3.Load())], keywords=[], ), )) else: for alias in node.names: # st['asname'] = modname.attr['name'] pos = pos_as_tuple(node) if pos is not None: attrname = ast3.Tuple( elts=[ast3.Str(s=alias.name), pos], ctx=ast3.Load()) # type: ast3.expr else: attrname = ast3.Str(s=alias.name) libs.append( ast3.Assign( targets=[ ast3.Subscript( value=ast3.Name(id='st', ctx=ast3.Load()), slice=ast3.Index(value=ast3.Str( s=alias.asname if alias. asname else alias.name), ), ctx=ast3.Store(), ), ], value=ast3.Subscript( value=ast3.Attribute( value=ast3.Name(id=module_name, ctx=ast3.Load()), attr='attr', ctx=ast3.Load(), ), slice=ast3.Index(value=attrname, ), ctx=ast3.Load(), ), )) else: if node.names[0].name == '*': # st.importStar() libs.append( ast3.Expr(value=ast3.Call( func=ast3.Attribute( value=ast3.Name(id='st', ctx=ast3.Load()), attr='importStar', ctx=ast3.Load(), ), args=[], keywords=[], ), )) else: libs.extend( ast3.parse( # type: ignore '\n'.join([ "st['{asname}'] = pt.Top".format( asname=alias.asname if alias.asname else alias. name) for alias in node.names ])).body) return libs
def visit_While(self, node: ast3.While) -> VisitorOutput: """Transforms an if statement into what Pytropos understands: For example, it converts:: while question: body into:: if_qstn = TRANSFORMED(question) def while_qst(st): return question def while_(st): body return st st = pt.runWhile(st, while_qstn, while_) """ if node.orelse: raise AstTransformerError( f"Pytropos doesn't support else statement in while loop yet, sorry :(" ) self.generic_visit(node) new_body = node.body.copy() new_body.append( ast3.Return(value=ast3.Name(id='st', ctx=ast3.Load()), )) new_node = [ ast3.FunctionDef( name='while_qst', args=ast3.arguments(args=[ast3.arg(arg='st', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[ast3.Return(value=node.test)], decorator_list=[], returns=None, ), ast3.FunctionDef( name='while_', args=ast3.arguments(args=[ast3.arg(arg='st', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=new_body, decorator_list=[], returns=None, ), ast3.Assign(targets=[ast3.Name(id='st', ctx=ast3.Store())], value=ast3.Call( func=ast3.Attribute( value=ast3.Name(id='pt', ctx=ast3.Load()), attr='runWhile', ctx=ast3.Load(), ), args=[ ast3.Name(id='st', ctx=ast3.Load()), ast3.Name(id='while_qst', ctx=ast3.Load()), ast3.Name(id='while_', ctx=ast3.Load()) ], keywords=[], )) ] return new_node # type: ignore
def visit_If(self, node: ast3.If) -> VisitorOutput: """Transforms an if statement into what Pytropos understands: For example, it converts:: if question: body1 else: body2 into:: if_qstn = TRANSFORMED(question) def if_(st): body1 return st def else_(st): body2 return st st = pt.runIf(st, if_qstn, if_, else_) """ self.generic_visit(node) new_body = node.body.copy() new_orelse = node.orelse.copy() orelse = bool(node.orelse) new_body.append( ast3.Return(value=ast3.Name(id='st', ctx=ast3.Load()), )) new_node = [ ast3.Assign(targets=[ast3.Name(id='if_qstn', ctx=ast3.Store())], value=node.test), ast3.FunctionDef( name='if_', args=ast3.arguments(args=[ast3.arg(arg='st', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=new_body, decorator_list=[], returns=None, ), ] if orelse: # adding "return st" new_orelse.append( ast3.Return(value=ast3.Name(id='st', ctx=ast3.Load()), )) new_node.append( ast3.FunctionDef( name='else_', args=ast3.arguments( args=[ast3.arg(arg='st', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=new_orelse, decorator_list=[], returns=None, )) new_node.append( ast3.Assign(targets=[ast3.Name(id='st', ctx=ast3.Store())], value=ast3.Call( func=ast3.Attribute( value=ast3.Name(id='pt', ctx=ast3.Load()), attr='runIf', ctx=ast3.Load(), ), args=[ ast3.Name(id='st', ctx=ast3.Load()), ast3.Name(id='if_qstn', ctx=ast3.Load()), ast3.Name(id='if_', ctx=ast3.Load()), ast3.Name(id='else_', ctx=ast3.Load()) ] if orelse else [ ast3.Name(id='st', ctx=ast3.Load()), ast3.Name(id='if_qstn', ctx=ast3.Load()), ast3.Name(id='if_', ctx=ast3.Load()) ], keywords=[], ))) return new_node # type: ignore
def visit_Subscript(self, node: ast3.Subscript): # A Subscript expression may be on the left-hand side of an assignment, but # the value field (which should have a hint) is in a Load context. if self._is_store(node): node.value.ctx = ast3.Store() self.generic_visit(node)
def expression_to_assignment(node: ast3.expr, *, name: str) -> ast3.Assign: name_node = ast3.copy_location(ast3.Name(name, ast3.Store()), node) result = ast3.Assign([name_node], node, None) return ast3.copy_location(result, node)