diff --git a/.gitignore b/.gitignore index da0bcebc84..8112fcc926 100644 --- a/.gitignore +++ b/.gitignore @@ -217,3 +217,10 @@ integration_tests/array_02_decl integration_tests/array_02_decl.c integration_tests/expr_12 integration_tests/expr_12.c +integration_tests/expr_07_generated.c +integration_tests/expr_07.c +expr_07.c +tests/c_interop1.c +tests/expr7.c +tests/loop1.c +tests/loop2.c diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 726c555dbd..57f5103a42 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -46,12 +46,13 @@ message("LPYTHON_RTLIB_LIBRARY: ${LPYTHON_RTLIB_LIBRARY}") macro(RUN) set(options FAIL) - set(oneValueArgs NAME IMPORT_PATH) + set(oneValueArgs NAME IMPORT_PATH CUSTOM_C_POSTPROCESSOR) set(multiValueArgs LABELS EXTRAFILES) cmake_parse_arguments(RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) set(name ${RUN_NAME}) set(import_path ${RUN_IMPORT_PATH}) + set(custom_c_postprocessor ${RUN_CUSTOM_C_POSTPROCESSOR}) if (NOT name) message(FATAL_ERROR "Must specify the NAME argument") endif() @@ -82,11 +83,20 @@ macro(RUN) set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE) endif() elseif(KIND STREQUAL "c") - add_custom_command( - OUTPUT ${name}.c - COMMAND lpython --show-c ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py > ${name}.c - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py - VERBATIM) + if (custom_c_postprocessor) + add_custom_command( + OUTPUT ${name}.c + COMMAND lpython --show-c ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py + --custom-c-postprocessor ${CMAKE_CURRENT_SOURCE_DIR}/${custom_c_postprocessor} -o ${name}.c + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py + VERBATIM) + else () + add_custom_command( + OUTPUT ${name}.c + COMMAND lpython --show-c ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py -o ${name}.c + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py + VERBATIM) + endif() add_executable(${name} ${name}.c ${RUN_EXTRAFILES}) set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C) target_link_libraries(${name} lpython_rtlib) @@ -209,7 +219,8 @@ RUN(NAME expr_03 LABELS cpython llvm c wasm) RUN(NAME expr_04 LABELS cpython llvm c) RUN(NAME expr_05 LABELS cpython llvm c) RUN(NAME expr_06 LABELS cpython llvm c) -RUN(NAME expr_07 LABELS cpython llvm c) +RUN(NAME expr_07 CUSTOM_C_POSTPROCESSOR test_c_custom_postprocessor.py + LABELS cpython llvm c) RUN(NAME expr_08 LABELS llvm c) RUN(NAME expr_09 LABELS cpython llvm c) RUN(NAME expr_10 LABELS cpython llvm c) diff --git a/integration_tests/test_c_custom_postprocessor.py b/integration_tests/test_c_custom_postprocessor.py new file mode 100644 index 0000000000..b43df435c1 --- /dev/null +++ b/integration_tests/test_c_custom_postprocessor.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import sys + +filename = sys.argv[1] + +file = open(filename, "r") +f = file.read() +file.close() + +file = open(filename, "w") +file.write(f) +file.close() diff --git a/src/bin/lpython.cpp b/src/bin/lpython.cpp index 53248156e7..2089cfc2e4 100644 --- a/src/bin/lpython.cpp +++ b/src/bin/lpython.cpp @@ -60,6 +60,12 @@ std::string remove_extension(const std::string& filename) { return filename.substr(0, lastdot); } +std::string extract_extension(const std::string& filename) { + size_t lastdot = filename.find_last_of("."); + if (lastdot == std::string::npos) return ""; + return filename.substr(lastdot); +} + std::string remove_path(const std::string& filename) { size_t lastslash = filename.find_last_of("/"); if (lastslash == std::string::npos) return filename; @@ -289,7 +295,35 @@ int emit_c(const std::string &infile, LFORTRAN_ASSERT(diagnostics.has_error()) return 3; } - std::cout << res.result; + + std::string output_file; + bool print_to_std = true; + if( !compiler_options.arg_o.empty() ) { + output_file = compiler_options.arg_o; + print_to_std = false; + } else { + output_file = remove_extension(infile) + ".c"; + } + std::string ccode_file = output_file; + std::ofstream ccode_out(ccode_file); + ccode_out << res.result; + ccode_out.close(); + if( !compiler_options.c_postprocessor_script.empty() ) { + std::string command = "python " + compiler_options.c_postprocessor_script + " " + ccode_file; + system(command.c_str()); + if( print_to_std ) { + std::ifstream ccode_in(ccode_file); + std::string modified_result = ""; + while( std::getline(ccode_in, modified_result) ) { + std::cout << modified_result << "\n"; + } + ccode_in.close(); + } + } else { + if( print_to_std ) { + std::cout << res.result; + } + } return 0; } @@ -1297,6 +1331,7 @@ int main(int argc, char *argv[]) bool show_errors = false; bool with_intrinsic_modules = false; std::string arg_pass; + std::string script_path; bool arg_no_color = false; bool show_llvm = false; bool show_asm = false; @@ -1363,6 +1398,7 @@ int main(int argc, char *argv[]) app.add_flag("--indent", compiler_options.indent, "Indented print ASR/AST"); app.add_flag("--tree", compiler_options.tree, "Tree structure print ASR/AST"); app.add_option("--pass", arg_pass, "Apply the ASR pass and show ASR (implies --show-asr)"); + app.add_option("--custom-c-postprocessor", script_path, "The script for processing the C code generated by --show-c command."); app.add_flag("--disable-main", compiler_options.disable_main, "Do not generate any code for the `main` function"); app.add_flag("--symtab-only", compiler_options.symtab_only, "Only create symbol tables in ASR (skip executable stmt)"); app.add_flag("--time-report", time_report, "Show compilation time report"); @@ -1485,6 +1521,17 @@ int main(int argc, char *argv[]) return 1; } + if( !script_path.empty() && extract_extension(script_path) != ".py" ) { + std::cerr << "Only a Python script is supported as a postprocessor for generated C code." << std::endl; + return 1; + } + + if( !script_path.empty() && !LFortran::path_exists(script_path) ) { + std::cerr << script_path << " is inaccessible or doesn't exist on your system." << std::endl; + return 1; + } + + compiler_options.c_postprocessor_script = script_path; if (arg_backend == "llvm") { backend = Backend::llvm; } else if (arg_backend == "cpp") { diff --git a/src/libasr/utils.h b/src/libasr/utils.h index bdec5e00fb..95b8d42a58 100644 --- a/src/libasr/utils.h +++ b/src/libasr/utils.h @@ -52,6 +52,7 @@ struct CompilerOptions { bool emit_debug_info = false; bool emit_debug_line_column = false; std::string import_path = ""; + std::string c_postprocessor_script = ""; Platform platform; CompilerOptions () : platform{get_platform()} {};