def test_variable_type_update(): tc = gc_scope_top_level() expr = "a" ctype = ctyp.terminal('int', False) v = crep.cpp_variable(expr, tc, ctype) v.update_type(ctyp.terminal('float', False)) assert v.cpp_type().type == 'float'
def test_deref_simple_ptr(): tc = gc_scope_top_level() expr = "a" c_type = ctyp.terminal('int', 1) v = crep.cpp_variable(expr, tc, c_type) d = crep.dereference_var(v) assert d.cpp_type().type == 'int' assert d.cpp_type().p_depth == 0 assert d.as_cpp() == '*a'
def test_variable_type__with_initial_update(): tc = gc_scope_top_level() expr = "a" c_type = ctyp.terminal('int', False) c_init = crep.cpp_value('0.0', tc, ctyp.terminal('int', False)) v = crep.cpp_variable(expr, tc, c_type, c_init) v.update_type(ctyp.terminal('float', False)) assert v.cpp_type().type == 'float' iv = v.initial_value() assert iv is not None assert iv.cpp_type().type == 'float'
async def exe_from_qastle(q: str): 'Dummy executor that will return the ast properly rendered. If qastle_roundtrip is true, then we will round trip the ast via qastle first.' # Round trip qastle if requested. import qastle a = qastle.text_ast_to_python_ast(q).body[0].value # Setup the rep for this filter from func_adl import find_EventDataset file = find_EventDataset(a) iterator = cpp_variable("bogus-do-not-use", top_level_scope(), cpp_type=None) set_rep(file, cpp_sequence(iterator, iterator, top_level_scope())) # Use the dummy executor to process this, and return it. exe = atlas_xaod_dummy_executor() exe.evaluate(a) return exe
def get_collection(self, md: EventCollectionSpecification, call_node: ast.Call): r''' Return a cpp ast for accessing the jet collection with the given arguments. ''' # Get the name jet collection to look at. if len(call_node.args) != 1: raise ValueError( f"Calling {md.name} - only one argument is allowed") if not isinstance(call_node.args[0], ast.Str): raise ValueError( f"Calling {md.name} - only acceptable argument is a string") # Fill in the CPP block next. r = cpp_ast.CPPCodeValue() r.args = [ 'collection_name', ] r.include_files += md.include_files r.link_libraries += md.libraries r.running_code += self.get_running_code(md.container_type) r.result = 'result' if issubclass(type(md.container_type), event_collection_collection_container): r.result_rep = lambda scope: crep.cpp_collection( unique_name(md.name.lower()), scope=scope, collection_type=md.container_type) # type: ignore else: r.result_rep = lambda scope: crep.cpp_variable( unique_name(md.name.lower()), scope=scope, cpp_type=md.container_type) # Replace it as the function that is going to get called. call_node.func = r # type: ignore return call_node
async def execute_result_async(self, a: ast.AST, title: str) -> Any: 'Dummy executor that will return the ast properly rendered. If qastle_roundtrip is true, then we will round trip the ast via qastle first.' # Round trip qastle if requested. if self._q_roundtrip: import qastle print(f'before: {ast.dump(a)}') a_text = qastle.python_ast_to_text_ast(a) a = qastle.text_ast_to_python_ast(a_text).body[0].value print(f'after: {ast.dump(a)}') # Setup the rep for this dataset from func_adl import find_EventDataset file = find_EventDataset(a) iterator = cpp_variable("bogus-do-not-use", top_level_scope(), cpp_type=None) set_rep(file, cpp_sequence(iterator, iterator, top_level_scope())) # Use the dummy executor to process this, and return it. exe = self.get_dummy_executor_obj() exe.evaluate(a) return exe
def isNonnullAst(call_node): r''' User is trying to test, on certian objects, if the object, when dereferenced, will be a null pointer. This is tricky for our data model because it treats an object as both a pointer and a object. Most of `func_adl_xAOD` is setup to deal with it as a pointer. This allows us to get around ths. ''' if len(call_node.args) != 1: raise ValueError("Calling isNonnull(object) has incorrect number of arguments") # Create an AST to hold onto all of this. r = cpp_ast.CPPCodeValue() # We need all four arguments pushed through. r.args = ['cms_object'] # The code is three steps r.running_code += ['auto result = (cms_object).isNonnull();'] r.result = 'result' r.result_rep = lambda scope: cpp_variable(unique_name('is_non_null'), scope=scope, cpp_type=ctyp.terminal('bool')) call_node.func = r return call_node
def write_cpp_files(self, ast: ast.AST, output_path: Path) -> ExecutionInfo: r""" Given the AST generate the C++ files that need to run. Return them along with the input files. """ # Find the base file dataset and mark it. from func_adl import find_EventDataset file = find_EventDataset(ast) iterator = crep.cpp_variable("bogus-do-not-use", top_level_scope(), cpp_type=None) crep.set_rep(file, crep.cpp_sequence(iterator, iterator, top_level_scope())) # Visit the AST to generate the code structure and find out what the # result is going to be. qv = self.get_visitor_obj() result_rep = qv.get_rep(ast) if _is_format_request(ast) \ else qv.get_as_ROOT(ast) # Emit the C++ code into our dictionaries to be used in template generation below. query_code = _cpp_source_emitter() qv.emit_query(query_code) book_code = _cpp_source_emitter() qv.emit_book(book_code) class_decl_code = qv.class_declaration_code() includes = qv.include_files() + self.body_include_files link_libraries = qv.link_libraries() + self.link_libraries # The replacement dict to pass to the template generator can now be filled info = {} info['query_code'] = query_code.lines_of_query_code() info['class_decl'] = class_decl_code info['book_code'] = book_code.lines_of_query_code() info['body_include_files'] = includes info['header_include_files'] = self.header_include_files info['private_members'] = self.private_members info['instance_initialization'] = self.instance_initialization info['initialize_lines'] = self.initialize_lines info['ctor_lines'] = self.ctor_lines info['link_libraries'] = link_libraries info.update(self.add_to_replacement_dict()) # We use jinja2 templates. Write out everything. template_dir = _find_dir(self._template_dir_name) j2_env = jinja2.Environment( loader=jinja2.FileSystemLoader(template_dir)) for file_name in self._file_names: self._copy_template_file(j2_env, info, file_name, output_path) (output_path / self._runner_name).chmod(0o755) # Reset our object for the next call (e.g. reset global state) self.reset() # Build the return object. return ExecutionInfo(result_rep, output_path, self._runner_name, self._file_names)
def build_CPPCodeValue(spec: CPPCodeSpecification, call_node: ast.Call) -> ast.Call: ''' Given the specification for a C++ code block, invoked as a function in our AST, replace the call node with a cpp spec callback AST so the C++ code is properly inserted into the call stream. Args: spec (CPPCodeSpecification): The specification, including the code, that we should insert at this call node call_node (ast.Call): The call node (with arguments) that we are going to replace with this C++ code. Raises: ValueError: Raised if something is wrong with the call site Returns: [type]: The C++ ast that can easily be emitted as code ''' if len(call_node.args) != len(spec.arguments): raise ValueError( f"The call of {spec.name}({', '.join(spec.arguments)}) has insufficient arguments ({len(call_node.args)})." ) if isinstance(call_node.func, ast.Attribute) and spec.method_object is None: raise ValueError( f"The {spec.name} is a function, but it is invoked like a method.") if isinstance(call_node.func, ast.Name) and spec.method_object is not None: raise ValueError( f"The {spec.name} is a method, but it is invoked like a function.") # Create an AST to hold onto all of this. r = CPPCodeValue() # We need TVector2 included here r.include_files += spec.include_files # We need all four arguments pushed through. r.args = spec.arguments # The code is three steps r.running_code += spec.code r.result = spec.result if spec.cpp_return_is_collection: r.result_rep = lambda scope: cpp_collection( unique_name(spec.name), scope=scope, collection_type=ctyp.collection(ctyp.terminal(spec.cpp_return_type) )) # type: ignore else: r.result_rep = lambda scope: cpp_variable(unique_name(spec.name), scope=scope, cpp_type=ctyp.terminal( spec.cpp_return_type)) # If this is a mehtod, copy the info over to generate the obj reference. if spec.method_object is not None: r.replacement_instance_obj = (spec.method_object, call_node.func.value.id) # type: ignore call_node.func = r # type: ignore return call_node