├── docs ├── sphinx │ ├── .gitignore │ ├── flpr-logo-sm.png │ ├── flpr_devel.rst │ ├── Makefile │ ├── app_devel.rst │ ├── index.rst │ ├── coding_practices.rst │ ├── why_flpr.rst │ ├── conf.py │ └── conformance.rst ├── flpr-logo.png ├── CMakeLists.txt ├── TODO.md ├── Smash_Hash.gperf ├── flpr-logo-white.svg ├── flpr-logo.svg └── flpr-logo-card.svg ├── .gitignore ├── src ├── CMakeLists.txt └── flpr │ ├── FLPRConfig.cmake.in │ ├── flpr.hh │ ├── LL_TT_Range.cc │ ├── Prgm_Tree.cc │ ├── Syntax_Tags.cc │ ├── File_Info.hh │ ├── utils.cc │ ├── Stmt_Parser_Exts.hh │ ├── Token_Text.cc │ ├── File_Info.cc │ ├── Stmt_Parser_Exts.cc │ ├── Stmt_Tree.cc │ ├── Stmt_Tree.hh │ ├── Parser_Result.hh │ ├── Syntax_Tags.hh │ ├── LL_Stmt_Src.cc │ ├── Indent_Table.cc │ ├── utils.hh │ ├── Line_Accum.hh │ ├── LL_TT_Range.hh │ ├── Indent_Table.hh │ ├── Label_Stack.hh │ ├── LL_Stmt.cc │ ├── Prgm_Tree.hh │ ├── LL_Stmt_Src.hh │ ├── Line_Accum.cc │ ├── CMakeLists.txt │ ├── Prgm_Parsers.hh │ ├── Token_Text.hh │ ├── Logical_File.hh │ ├── Procedure_Visitor.hh │ └── LL_Stmt.hh ├── NOTICE ├── apps ├── FLPRAppConfig.cmake.in ├── Timer.hh ├── flpr-format.cc ├── module_base.hh ├── CMakeLists.txt ├── parse_files.cc ├── flpr_show_cst.cc ├── flpr_format_base.hh ├── module.cc ├── ext_demo.cc └── module_base.cc ├── CMakeLists.txt ├── tests ├── LL_Helper.cc ├── test_logical_file.cc ├── LL_Helper.hh ├── CMakeLists.txt ├── test_utils.cc ├── test_label_stack.cc ├── test_line_accum.cc ├── test_syntag_sanity.cc ├── parse_helpers.hh ├── test_safe_list.cc └── test_tt_stream.cc ├── cmake ├── BuildType.cmake └── CompilerFlags.cmake ├── README.md ├── LICENSE ├── CODE_OF_CONDUCT.md └── .clang-format /docs/sphinx/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | *~ 3 | compile_commands.json 4 | 5 | -------------------------------------------------------------------------------- /docs/flpr-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/FLPR/HEAD/docs/flpr-logo.png -------------------------------------------------------------------------------- /docs/sphinx/flpr-logo-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/FLPR/HEAD/docs/sphinx/flpr-logo-sm.png -------------------------------------------------------------------------------- /docs/sphinx/flpr_devel.rst: -------------------------------------------------------------------------------- 1 | .. _flpr_devel: 2 | 3 | ======================== 4 | FLPR Library Development 5 | ======================== 6 | 7 | Notes on extending the FLPR library itself. 8 | 9 | coming soon... 10 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # FLPR/src/CMakeLists.txt 11 | 12 | add_subdirectory(flpr) 13 | 14 | -------------------------------------------------------------------------------- /docs/sphinx/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # FLPR/docs/CMakeLists.txt 11 | 12 | find_package(Doxygen) 13 | set(DOXYGEN_GENERATE_MAN no) 14 | set(DOXYGEN_RECURSIVE NO) 15 | set(DOXYGEN_FULL_PATH_NAMES NO) 16 | doxygen_add_docs( 17 | doxygen 18 | ${PROJECT_SOURCE_DIR}/src/flpr 19 | ) 20 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | © (or copyright) 2019-2020. Triad National Security, LLC. All rights reserved. 2 | 3 | This program was produced under U.S. Government contract 89233218CNA000001 4 | for Los Alamos National Laboratory (LANL), which is operated by Triad 5 | National Security, LLC for the U.S. Department of Energy/National Nuclear 6 | Security Administration. All rights in the program are reserved by Triad 7 | National Security, LLC, and the U.S. Department of Energy/National Nuclear 8 | Security Administration. The Government is granted for itself and others 9 | acting on its behalf a nonexclusive, paid-up, irrevocable worldwide license 10 | in this material to reproduce, prepare derivative works, distribute copies 11 | to the public, perform publicly and display publicly, and to permit others 12 | to do so. 13 | -------------------------------------------------------------------------------- /src/flpr/FLPRConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | set(FLPR_VERSION @PROJECT_VERSION@) 11 | 12 | @PACKAGE_INIT@ 13 | 14 | include("${CMAKE_CURRENT_LIST_DIR}/FLPRTargets.cmake") 15 | set_and_check(FLPR_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") 16 | set_and_check(FLPR_LIB_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@") 17 | 18 | check_required_components(flpr) 19 | -------------------------------------------------------------------------------- /apps/FLPRAppConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | set(FLPR_VERSION @PROJECT_VERSION@) 11 | 12 | @PACKAGE_INIT@ 13 | 14 | include("${CMAKE_CURRENT_LIST_DIR}/FLPRAppTargets.cmake") 15 | set_and_check(FLPR_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") 16 | set_and_check(FLPR_LIB_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@") 17 | 18 | check_required_components(flprapp) 19 | -------------------------------------------------------------------------------- /src/flpr/flpr.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file flpr.hh 13 | 14 | The master high-level include file for FLPR applications 15 | */ 16 | #ifndef FLPR_FLPR_HH 17 | #define FLPR_FLPR_HH 1 18 | 19 | #include "flpr/Parsed_File.hh" 20 | #include "flpr/Procedure.hh" 21 | #include "flpr/Procedure_Visitor.hh" 22 | #include "flpr/Stmt_Parser_Exts.hh" 23 | #include "flpr/utils.hh" 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/flpr/LL_TT_Range.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file LL_TT_Range.cc 13 | 14 | Definition of non-inline LL_TT_Range member functions. 15 | */ 16 | 17 | #include "flpr/LL_TT_Range.hh" 18 | 19 | namespace FLPR { 20 | std::ostream &LL_TT_Range::print(std::ostream &os) const { 21 | if (empty()) { 22 | os << "EMPTY"; 23 | } else { 24 | for (auto const &t : *this) { 25 | os << t << ' '; 26 | } 27 | } 28 | return os; 29 | } 30 | 31 | } // namespace FLPR 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # FLPR/CMakeLists.txt 11 | 12 | cmake_minimum_required (VERSION 3.12) 13 | project (FLPR VERSION 0.1.0 LANGUAGES CXX) 14 | 15 | # Allow CMake to find some project include files 16 | list (INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 17 | 18 | include(BuildType) 19 | include(CompilerFlags) 20 | 21 | option(BUILD_SHARED_LIBS "build shared libraries (default=static)" false) 22 | 23 | enable_testing() 24 | add_subdirectory(src) 25 | add_subdirectory(tests) 26 | add_subdirectory(docs) 27 | add_subdirectory(apps) 28 | -------------------------------------------------------------------------------- /tests/LL_Helper.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #include "LL_Helper.hh" 12 | #include "parse_helpers.hh" 13 | 14 | LL_Helper::LL_Helper(Raw_Lines &&buf) noexcept { 15 | text_.scan_free(buf); 16 | assert(!text_.lines.empty()); 17 | text_.make_stmts(); 18 | } 19 | 20 | LL_Helper::LL_Helper(Raw_Lines &&buf, int const last_col) noexcept { 21 | text_.scan_fixed(buf, last_col); 22 | assert(!text_.lines.empty()); 23 | text_.make_stmts(); 24 | } 25 | 26 | std::ostream &LL_Helper::print(std::ostream &os) const { 27 | for (auto const &l : text_.lines) { 28 | l.dump(os); 29 | } 30 | return os; 31 | } 32 | -------------------------------------------------------------------------------- /cmake/BuildType.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # Taken from: 11 | # https://blog.kitware.com/cmake-and-the-default-build-type/ 12 | 13 | # Set a default build type if none was specified 14 | set(default_build_type "Debug") 15 | 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | message(STATUS "Setting build type to '${default_build_type}' as none was specified.") 18 | set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE 19 | STRING "Choose the type of build." FORCE) 20 | # Set the possible values of build type for cmake-gui 21 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 22 | "Debug" "Release") 23 | endif() 24 | -------------------------------------------------------------------------------- /src/flpr/Prgm_Tree.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Prgm_Tree.cc 13 | */ 14 | 15 | #include "flpr/Prgm_Tree.hh" 16 | #include "flpr/Prgm_Parsers.hh" 17 | 18 | namespace FLPR { 19 | namespace Prgm { 20 | 21 | std::ostream &operator<<(std::ostream &os, Prgm_Node_Data const &pn) { 22 | if (pn.is_stmt()) { 23 | if (pn.stmt_tree()) { 24 | os << '[' << pn.stmt_tree() << ']'; 25 | } 26 | } else { 27 | Syntax_Tags::print(os, pn.syntag()); 28 | } 29 | return os; 30 | } 31 | 32 | // do an explicit instantiation of the default Prgm::Parsers 33 | template struct Parsers<>; 34 | 35 | } // namespace Prgm 36 | } // namespace FLPR 37 | -------------------------------------------------------------------------------- /cmake/CompilerFlags.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # Sets up the project defaults for compiler flags 11 | 12 | # Setting the CMAKE_*_FLAGS_* is a cmake antipattern. Should fix that. 13 | 14 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 15 | set(CMAKE_C_FLAGS_DEBUG "-Wall -Wextra -Wno-unused-parameter -g -O0") 16 | set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Wextra -Wno-unused-parameter -Wdeprecated -g -O0") 17 | set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") 18 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") 19 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") 20 | set(CMAKE_C_FLAGS_DEBUG "-w2 -g -O0") 21 | set(CMAKE_CXX_FLAGS_DEBUG "-w2 -g -O0") 22 | set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") 23 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") 24 | endif() 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/sphinx/app_devel.rst: -------------------------------------------------------------------------------- 1 | .. _app_devel: 2 | 3 | ============================ 4 | FLPR Application Development 5 | ============================ 6 | 7 | This (very incomplete) section discusses designing and developing an 8 | application using FLPR. 9 | 10 | -------------------------- 11 | Types of FLPR Applications 12 | -------------------------- 13 | 14 | We tend to categorize FLPR-based applications into two modes of 15 | operation: one-shot vs. continuous transformations. By "one-shot" 16 | transformation, we mean that the application is used to transform the 17 | source code once, and the resultant code is used by developers from 18 | that point forward (i.e. committed to a repository). An example of a 19 | one-shot code transformations are code modernization tasks, such as 20 | transforming ``real*8`` to ``real(kind=k)``. By "continuous" 21 | transformation, we mean that an application is called as a souce 22 | preprocessor before each and every compilation pass. Continuous 23 | transformation may be used to translate application-specific 24 | extensions to Fortran, or to inject compiler-specific optimization 25 | directives. FLPR is intended to make writing one-shot utilities 26 | quick and easy, while giving you the power to build production-level 27 | tools to include in your system. 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/flpr/Syntax_Tags.cc: -------------------------------------------------------------------------------- 1 | #include "Syntax_Tags.hh" 2 | #include 3 | 4 | /* Declare the static member variable */ 5 | std::vector FLPR::Syntax_Tags::extensions_; 6 | 7 | std::string FLPR::Syntax_Tags::label(int const syntag) { 8 | const int ext_idx = get_ext_idx_(syntag); 9 | if (ext_idx == -2) 10 | return strings_[syntag]; 11 | if (ext_idx == -1) { 12 | std::string tmp = 13 | "'; 14 | return tmp; 15 | } 16 | return extensions_[ext_idx].label; 17 | } 18 | 19 | int FLPR::Syntax_Tags::type(int const syntag) { 20 | const int ext_idx = get_ext_idx_(syntag); 21 | if (ext_idx == -2) 22 | return types_[syntag]; 23 | if (ext_idx == -1) 24 | return 4; 25 | return extensions_[ext_idx].type; 26 | } 27 | 28 | bool FLPR::Syntax_Tags::register_ext(int const tag_idx, char const *const label, 29 | int const type) { 30 | assert(tag_idx >= CLIENT_EXTENSION); 31 | const size_t ext_idx = static_cast(tag_idx - CLIENT_EXTENSION); 32 | if (extensions_.size() <= ext_idx) { 33 | extensions_.resize(ext_idx + 1); 34 | } 35 | assert(extensions_[ext_idx].empty()); 36 | extensions_[ext_idx].label = std::string(label); 37 | extensions_[ext_idx].type = type; 38 | return true; 39 | } 40 | -------------------------------------------------------------------------------- /tests/test_logical_file.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #include "LL_Helper.hh" 12 | #include "flpr/Logical_File.hh" 13 | #include "test_helpers.hh" 14 | #include 15 | #include 16 | 17 | using FLPR::LL_SEQ; 18 | using FLPR::LL_STMT_SEQ; 19 | using FLPR::Logical_File; 20 | 21 | bool replace_stmt_text_1() { 22 | // clang-format off 23 | LL_Helper helper({" return"}); 24 | // clang-format on 25 | Logical_File &file = helper.logical_file(); 26 | TEST_INT(file.ll_stmts.size(), 1); 27 | LL_STMT_SEQ::iterator stmt_it = file.ll_stmts.begin(); 28 | TEST_INT(stmt_it->size(), 1); 29 | LL_SEQ::iterator orig_ll_it = stmt_it->it(); 30 | file.replace_stmt_text(stmt_it, {"RETURN"}, 31 | FLPR::Syntax_Tags::SG_RETURN_STMT); 32 | TEST_INT(file.ll_stmts.size(), 1); 33 | TEST_TRUE(file.ll_stmts.begin() == stmt_it); 34 | TEST_TRUE(stmt_it->it() == orig_ll_it); 35 | 36 | TEST_INT(file.ll_stmts.front().size(), 1); 37 | return true; 38 | } 39 | 40 | int main() { 41 | TEST_MAIN_DECL; 42 | TEST(replace_stmt_text_1); 43 | TEST_MAIN_REPORT; 44 | } 45 | -------------------------------------------------------------------------------- /src/flpr/File_Info.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file File_Info.hh 13 | */ 14 | 15 | #ifndef FLPR_FILE_INFO_HH 16 | #define FLPR_FILE_INFO_HH 1 17 | 18 | #include 19 | #include 20 | 21 | //! Namespace for FLPR: The Fortran Language Program Remodeling system 22 | namespace FLPR { 23 | //! The type of a Logical_File 24 | enum class File_Type { 25 | UNKNOWN, 26 | FIXEDFMT, //!< .f or .F file 27 | FREEFMT //!< .f90 file 28 | }; 29 | 30 | //! Basic information about an actual file 31 | struct File_Info { 32 | 33 | File_Info(std::string const &filename, 34 | File_Type file_type = File_Type::UNKNOWN); 35 | 36 | //! The name of the file that was scanned 37 | std::string filename; 38 | //! The language level in this file. 39 | File_Type file_type; 40 | //! If fixed-format, the last column 41 | int last_fixed_column = 0; 42 | }; 43 | 44 | //! Guess the type of a file from its extension 45 | File_Type file_type_from_extension(std::string const &filename); 46 | 47 | std::ostream &operator<<(std::ostream &os, File_Type ft); 48 | 49 | } // namespace FLPR 50 | #endif 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![flipper-logo](docs/flpr-logo.png) 2 | # FLPR: The Fortran Language Program Remodeling system 3 | 4 | The *Fortran Language Program Remodeling* system, or FLPR, is a C++17 5 | library for manipulating Fortran source code. This package contains: 6 | - A "best effort" Fortran 2018 input parser for fixed and free 7 | form inputs. 8 | - Data structures for manipulating source code at the program unit, 9 | statement, and physical line levels. 10 | - Sample applications (in the `apps/` directory) that illustrate usage 11 | and provide some ideas as to how you could use the library. 12 | 13 | 14 | ## Getting started 15 | 16 | Please see the documentation on [Read the 17 | Docs](https://flpr.readthedocs.io), or in the 18 | [getting started](docs/sphinx/getting_started.rst) file. 19 | 20 | 21 | Any questions? Please file an issue on GitHub, or email . 22 | 23 | ## Project status 24 | 25 | FLPR is undergoing active development, and isn't ready for a formal 26 | release yet. In general, the default `develop` branch is expected to 27 | be functional at all times, but all other branches are "as-is." Feel 28 | free to fork FLPR and submit PRs for new features or fixes, but please 29 | file an issue in the tracker before starting, just so we know what 30 | people are thinking about. 31 | 32 | ## Authors 33 | 34 | FLPR was created by Paul Henning (). 35 | 36 | 37 | ## Release 38 | 39 | FLPR is released as open source under a BSD-3 License. For more 40 | details, please see the [LICENSE](LICENSE) and [NOTICE](NOTICE) files. 41 | 42 | `LANL C19039` 43 | 44 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This program is open source under the BSD-3 License. 2 | 3 | © (or copyright) 2019-2020. Triad National Security, LLC. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | -------------------------------------------------------------------------------- /tests/LL_Helper.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #ifndef LL_HELPER_HH 12 | #define LL_HELPER_HH 1 13 | 14 | #include "flpr/LL_Stmt_Src.hh" 15 | #include "flpr/Logical_File.hh" 16 | #include "flpr/TT_Stream.hh" 17 | #include 18 | 19 | //! Logical_Line helper 20 | /*! This forms a FLPR::Logical_File structure by parsing the strings given in \c 21 | buf member of the constructor. The file contents can then be accessed through 22 | an FLPR::LL_Stmt_Src provided by src(). For convenience, \c stream1() returns a 23 | FLPR::TT_Stream for the first statement. */ 24 | class LL_Helper { 25 | public: 26 | using Raw_Lines = FLPR::Logical_File::Line_Buf; 27 | explicit LL_Helper(Raw_Lines &&buf) noexcept; 28 | LL_Helper(Raw_Lines &&buf, int const last_col) noexcept; 29 | //! Return a TT_Stream for the first statement 30 | FLPR::TT_Stream stream1() noexcept { 31 | return FLPR::TT_Stream{text_.ll_stmts.front()}; 32 | } 33 | constexpr FLPR::LL_SEQ &lines() { return text_.lines; } 34 | constexpr FLPR::LL_STMT_SEQ &ll_stmts() { return text_.ll_stmts; } 35 | //! dump the contents of the Logical_File 36 | std::ostream &print(std::ostream &os) const; 37 | constexpr FLPR::Logical_File &logical_file() { return text_; } 38 | 39 | private: 40 | FLPR::Logical_File text_; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /docs/sphinx/index.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Welcome to FLPR! 3 | ================ 4 | 5 | .. epigraph:: 6 | **flipper** *noun* 7 | 8 | 1. A component of pinball machines, which were a staple of 1970s arcades 9 | but still have ardent fans. 10 | 2. One who renovates neglected properties for profit. 11 | 12 | ------------ 13 | Introduction 14 | ------------ 15 | 16 | The *Fortran Language Program Remodeling* system, or FLPR, is a C++ 17 | library for manipulating Fortran source code. You could use FLPR to 18 | quickly develop small code maintenance utilities, or to build 19 | something as ambitious as a source-to-source translation tool to 20 | implement language extensions for your project. 21 | 22 | ------------------------------- 23 | Distinguishing features of FLPR 24 | ------------------------------- 25 | 26 | - Layout and comment management: 27 | 28 | - Gives developers recognizable code when debugging transformed source. 29 | - Preserves important knowledge stored in comments. 30 | 31 | - Proper extensible grammar: 32 | 33 | - Better than regexp at recognizing complex structures. 34 | - Allows implementation of application-specific language features. 35 | 36 | - Compact API allows you to quickly write "disposable" utilities for 37 | doing specific code transformations. 38 | 39 | 40 | 41 | Any questions? Please file an issue on GitHub, or email 42 | flpr-dev@lanl.gov 43 | 44 | 45 | ------------------------------------------------------------------------------- 46 | 47 | .. toctree:: 48 | :maxdepth: 2 49 | :caption: Basics 50 | 51 | getting_started 52 | coding_practices 53 | 54 | .. toctree:: 55 | :maxdepth: 2 56 | :caption: Using FLPR 57 | 58 | app_devel 59 | flpr_entities 60 | flpr_devel 61 | 62 | .. toctree:: 63 | :maxdepth: 2 64 | :caption: FAQs 65 | 66 | conformance 67 | why_flpr 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/flpr/utils.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file utils.cc 13 | */ 14 | 15 | #include "flpr/utils.hh" 16 | #include 17 | #include 18 | 19 | namespace FLPR { 20 | /* ------------------------------------------------------------------------ */ 21 | void tolower(std::string &foo) { 22 | for (std::string::size_type i = 0; i < foo.size(); ++i) 23 | foo[i] = std::tolower(foo[i]); 24 | } 25 | 26 | /* ------------------------------------------------------------------------ */ 27 | void toupper(std::string &foo) { 28 | for (std::string::size_type i = 0; i < foo.size(); ++i) 29 | foo[i] = std::toupper(foo[i]); 30 | } 31 | 32 | /* ------------------------------------------------------------------------ */ 33 | void simple_tokenize(std::string const &s, std::vector &t, 34 | const std::string::value_type delim) { 35 | std::string::const_iterator sp = s.begin(); 36 | do { 37 | std::string::const_iterator tbeg = sp; 38 | while (s.end() != sp && delim != *sp) 39 | ++sp; 40 | if (tbeg != sp) 41 | t.push_back(std::string(tbeg, sp)); 42 | } while (s.end() != sp++); 43 | } 44 | 45 | /* ------------------------------------------------------------------------ */ 46 | void print_ctrlchars(std::ostream &os, const std::string &s) { 47 | for (auto c : s) { 48 | if (c < 32) 49 | os << std::hex << std::showbase << int(c); 50 | else 51 | os << c; 52 | } 53 | } 54 | 55 | } // namespace FLPR 56 | -------------------------------------------------------------------------------- /src/flpr/Stmt_Parser_Exts.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Stmt_Parser_Exts.hh 13 | */ 14 | 15 | #ifndef FLPR_STMT_PARSER_EXTS_HH 16 | #define FLPR_STMT_PARSER_EXTS_HH 1 17 | 18 | #include "flpr/Stmt_Parsers.hh" 19 | #include "flpr/Stmt_Tree.hh" 20 | #include 21 | 22 | namespace FLPR { 23 | namespace Stmt { 24 | //! Manage extensions for the statement parsers 25 | /*! The Parser_Exts class is (optionally) used to provide application-specific 26 | statement parsers. This allows the client application to extend the 27 | language being recognized in Prgm::Parsers */ 28 | class Parser_Exts { 29 | public: 30 | //! The statement parser function signature 31 | /*! Note that this is the same as what you find in parse_stmt.hh */ 32 | using stmt_parser = Stmt_Tree (*)(TT_Stream &ts); 33 | 34 | public: 35 | //! Register an action-stmt extension 36 | void register_action_stmt(stmt_parser ext) noexcept; 37 | //! Register an other-specification-stmt extension 38 | void register_other_specification_stmt(stmt_parser ext) noexcept; 39 | //! Clear all registered extensions 40 | void clear() noexcept; 41 | 42 | //@{ 43 | /*! This is called by a driver routine in parse_stmt.cc and is not intended 44 | for client use */ 45 | SP_Result parse_action_stmt(TT_Stream &ts); 46 | SP_Result parse_other_specification_stmt(TT_Stream &ts); 47 | //@} 48 | private: 49 | std::vector action_exts_; 50 | std::vector other_specification_exts_; 51 | }; 52 | 53 | //! Access the Parser_Exts singleton 54 | Parser_Exts &get_parser_exts() noexcept; 55 | 56 | } // namespace Stmt 57 | } // namespace FLPR 58 | #endif 59 | -------------------------------------------------------------------------------- /src/flpr/Token_Text.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Token_Text.cc 13 | */ 14 | 15 | #include "flpr/Token_Text.hh" 16 | #include "flpr/Syntax_Tags.hh" 17 | #include 18 | #include 19 | #include 20 | 21 | namespace FLPR { 22 | 23 | Token_Text::Token_Text() 24 | : token(Syntax_Tags::BAD), start_line(-1), start_pos(-1) {} 25 | 26 | std::string const &Token_Text::lower() const { 27 | // Lazy construction 28 | if (lower_.empty()) { 29 | lower_.resize(text_.size()); 30 | std::transform(text_.begin(), text_.end(), lower_.begin(), 31 | [](unsigned char c) { return std::tolower(c); }); 32 | } 33 | return lower_; 34 | } 35 | 36 | std::ostream &operator<<(std::ostream &os, Token_Text const &tt) { 37 | return Syntax_Tags::print(os, tt.token) 38 | << ":\"" << tt.text() << "\" (" << tt.start_line << '.' << tt.start_pos 39 | << ')'; 40 | } 41 | 42 | void render(std::ostream &os, TT_SEQ::const_iterator beg, 43 | TT_SEQ::const_iterator end) { 44 | if (beg == end) 45 | return; 46 | TT_SEQ::const_iterator next = std::next(beg); 47 | for (; next != end; ++beg, ++next) { 48 | os << beg->text(); 49 | int spaces = std::max(beg->post_spaces(), next->pre_spaces()); 50 | if (spaces > 0) 51 | os << std::setw(spaces) << ' '; 52 | } 53 | os << beg->text(); 54 | } 55 | 56 | void unkeyword(TT_SEQ::iterator beg, const TT_SEQ::iterator end, int first_N) { 57 | while (beg != end && first_N--) { 58 | if (Syntax_Tags::is_keyword(beg->token)) 59 | beg->token = Syntax_Tags::TK_NAME; 60 | beg++; 61 | } 62 | } 63 | 64 | } // namespace FLPR 65 | -------------------------------------------------------------------------------- /apps/Timer.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #ifndef TIMER_HH 12 | #define TIMER_HH 1 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class Timer { 20 | using CLOCK_T = std::chrono::system_clock; 21 | using REP_T = double; 22 | using DURATION_T = std::chrono::duration; 23 | 24 | public: 25 | constexpr Timer() : running_{false}, accum_{DURATION_T::zero()} {} 26 | void start() { 27 | assert(!running_); 28 | running_ = true; 29 | start_tp_ = CLOCK_T::now(); 30 | } 31 | void stop() { 32 | std::chrono::time_point const stop_tp = CLOCK_T::now(); 33 | assert(running_); 34 | running_ = false; 35 | accum_ += stop_tp - start_tp_; 36 | } 37 | constexpr REP_T seconds() const { 38 | assert(!running_); 39 | return accum_.count(); 40 | } 41 | constexpr void clear() { 42 | running_ = false; 43 | accum_ = DURATION_T::zero(); 44 | } 45 | std::ostream &print(std::ostream &os) const { 46 | auto prec = os.precision(3); 47 | double s = seconds(); 48 | if (s > 60) { 49 | double const m = std::floor(s / 60.0); 50 | s -= (m * 60); 51 | os << static_cast(m) << 'm'; 52 | } 53 | if (s < 0.0001) 54 | os << s * 1000000.0 << "us"; 55 | else if (s < 0.1) 56 | os << s * 1000.0 << "ms"; 57 | else 58 | os << s << 's'; 59 | os.precision(prec); 60 | return os; 61 | } 62 | 63 | private: 64 | bool running_; 65 | DURATION_T accum_; 66 | std::chrono::time_point start_tp_; 67 | }; 68 | 69 | inline std::ostream &operator<<(std::ostream &os, const Timer &t) { 70 | return t.print(os); 71 | } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/flpr/File_Info.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file File_Info.cc 13 | */ 14 | 15 | #include "flpr/File_Info.hh" 16 | #include 17 | 18 | namespace FLPR { 19 | 20 | File_Info::File_Info(std::string const &filename, File_Type file_type_in) 21 | : filename{filename}, file_type{file_type_in} { 22 | if (File_Type::UNKNOWN == file_type) { 23 | file_type = file_type_from_extension(filename); 24 | } 25 | } 26 | 27 | std::ostream &operator<<(std::ostream &os, File_Type ft) { 28 | switch (ft) { 29 | case File_Type::UNKNOWN: 30 | os << "unknown format"; 31 | break; 32 | case File_Type::FIXEDFMT: 33 | os << "fixed-format"; 34 | break; 35 | case File_Type::FREEFMT: 36 | os << "free-format"; 37 | break; 38 | } 39 | return os; 40 | } 41 | 42 | /*! Guess the type of a file from its extension */ 43 | FLPR::File_Type file_type_from_extension(std::string const &filename) { 44 | using FLPR::File_Type; 45 | File_Type file_type{File_Type::UNKNOWN}; 46 | 47 | const size_t ptpos = filename.find_last_of('.'); 48 | if (std::string::npos == ptpos) { 49 | std::cerr << "FLPR::file_type_from_extension: Error: unable to find " 50 | << "filename extension for \"" << filename << "\"\n"; 51 | } else { 52 | const std::string extension = filename.substr(ptpos + 1); 53 | if (extension == "f" || extension == "F" || extension == "F90") { 54 | file_type = File_Type::FIXEDFMT; 55 | } else if (extension == "f90") { 56 | file_type = File_Type::FREEFMT; 57 | } else { 58 | std::cerr << "FLPR::file_type_from_extension: Error: unrecognized " 59 | << "filename extension \"" << extension << "\" for \"" 60 | << filename << "\"\n"; 61 | } 62 | } 63 | return file_type; 64 | } 65 | } // namespace FLPR 66 | -------------------------------------------------------------------------------- /docs/sphinx/coding_practices.rst: -------------------------------------------------------------------------------- 1 | .. _coding_practices: 2 | 3 | ================================= 4 | Coding Practices For Contributors 5 | ================================= 6 | 7 | This page provides information on project expectations for C++ coding 8 | practices. Contributors should check that these practices are followed 9 | before submitting a pull request. 10 | 11 | .. contents:: 12 | 13 | 14 | --------------- 15 | C++ Code Layout 16 | --------------- 17 | 18 | FLPR uses ``clang-format`` to manage C++ code layout. There is a 19 | ``.clang-format`` file provided in the root directory, which is a copy 20 | of the LLVM style layout. Please run ``clang-format`` before commiting 21 | code: other developers should be able to run ``git pull``, then 22 | ``clang-format``, and see only their own changes. 23 | 24 | 25 | -------------------------------------- 26 | C++ Language Standards and Portability 27 | -------------------------------------- 28 | 29 | It is expected that the FLPR code base can be compiled with a C++ '17 30 | compliant compiler, without compiler-specific extensions. If 31 | possible, please try both current GCC and LLVM compilers for 32 | portability. 33 | 34 | ------------------ 35 | Code Documentation 36 | ------------------ 37 | 38 | Adding Doxygen documentation blocks for files and functions is 39 | encouraged. Please use the ``Qt`` style of block comment without 40 | intermediate "*":: 41 | 42 | /*! 43 | \file somename.cc 44 | ... more doxygen text ... 45 | */ 46 | 47 | 48 | ---------- 49 | File Names 50 | ---------- 51 | 52 | The following file name extensions are to be used: 53 | 54 | .hh & .cc 55 | C++ language header and source files 56 | 57 | .l 58 | Flex input 59 | 60 | .md 61 | Markdown-style documentation 62 | 63 | .rst 64 | Restructured Text-style documentation 65 | 66 | File names should not depend on case-sensitivity for uniqueness. 67 | 68 | -------------- 69 | Include Guards 70 | -------------- 71 | 72 | The code section of a C++ header file should be wrapped with an include 73 | guard ``#ifdef``. For the file ``FLPR/src/flpr/Stmt_Tree.hh``, the 74 | guards should look like:: 75 | 76 | #ifndef FLPR_STMT_TREE_HH 77 | #define FLPR_STMT_TREE_HH 1 78 | 79 | ... declarations ... 80 | 81 | #endif 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # FLPR/src/tests/CMakeLists.txt 11 | 12 | # Make a common test helper library 'flpr_test' 13 | add_library(flpr_test test_utils.cc LL_Helper.cc) 14 | target_compile_features(flpr_test PUBLIC cxx_std_17) 15 | set_target_properties(flpr_test PROPERTIES CXX_EXTENSIONS OFF) 16 | target_link_libraries(flpr_test flpr) 17 | target_include_directories(flpr_test 18 | PRIVATE 19 | $ 20 | $ 21 | $ 22 | ) 23 | 24 | # Add any tests to this list. These are roughly ordered in terms of their 25 | # place in the hierarchy of data structures. 26 | set(TEST_EXE 27 | "test_safe_list" 28 | "test_tree" 29 | "test_label_stack" 30 | "test_file_line" 31 | "test_line_accum" 32 | "test_syntag_sanity" 33 | "test_logical_line" 34 | "test_logical_file" 35 | "test_tt_stream" 36 | "test_stmt_cover" 37 | "test_parse_stmt" 38 | "test_parse_substmt" 39 | "test_parse_type_decl" 40 | "test_parse_prgm" 41 | ) 42 | 43 | # Create tests from each entry in TEST_EXE 44 | foreach(e IN LISTS TEST_EXE) 45 | add_executable("${e}" "${e}.cc") 46 | target_link_libraries(${e} flpr_test flpr) 47 | target_include_directories(${e} 48 | PRIVATE 49 | $ 50 | $ 51 | ) 52 | add_test(NAME "${e}" COMMAND "${e}") 53 | endforeach(e) 54 | 55 | # Add in a new test target called "check" that rebuilds test files first 56 | # You can extend this command to cover tests in a parent package by using 57 | # "add_dependencies(check ${list_of_test_names})" 58 | add_custom_target(check 59 | COMMAND ${CMAKE_CTEST_COMMAND} 60 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 61 | DEPENDS ${TEST_EXE} 62 | ) 63 | -------------------------------------------------------------------------------- /tests/test_utils.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #include "flpr/utils.hh" 12 | #include "test_helpers.hh" 13 | 14 | bool no_trim() { 15 | const std::string str("no leading/trailing ws"); 16 | const std::string res = FLPR::trim(str); 17 | TEST_STR("no leading/trailing ws", res); 18 | return true; 19 | } 20 | 21 | bool trim_tail() { 22 | const std::string str("trailing ws "); 23 | const std::string res = FLPR::trim(str); 24 | TEST_STR("trailing ws", res); 25 | return true; 26 | } 27 | 28 | bool trim_head() { 29 | const std::string str(" leading ws"); 30 | const std::string res = FLPR::trim(str); 31 | TEST_STR("leading ws", res); 32 | return true; 33 | } 34 | 35 | bool trim_both() { 36 | const std::string str(" both "); 37 | const std::string res = FLPR::trim(str); 38 | TEST_STR("both", res); 39 | return true; 40 | } 41 | 42 | bool test_lower() { 43 | const std::string str(" ThIS 1s a TEST "); 44 | std::string res = str; 45 | FLPR::tolower(res); 46 | TEST_STR(" this 1s a test ", res); 47 | return true; 48 | } 49 | 50 | bool test_upper() { 51 | const std::string str(" ThIS 1s a TEST "); 52 | std::string res = str; 53 | FLPR::toupper(res); 54 | TEST_STR(" THIS 1S A TEST ", res); 55 | return true; 56 | } 57 | 58 | bool test_simple_tokenize1() { 59 | const std::string str(" this is a test"); 60 | std::vector res; 61 | FLPR::simple_tokenize(str, res); 62 | TEST_EQ(4, res.size()); 63 | TEST_STR("this", res[0]); 64 | TEST_STR("is", res[1]); 65 | TEST_STR("a", res[2]); 66 | TEST_STR("test", res[3]); 67 | return true; 68 | } 69 | 70 | int main() { 71 | TEST_MAIN_DECL; 72 | TEST(no_trim); 73 | TEST(trim_tail); 74 | TEST(trim_head); 75 | TEST(trim_both); 76 | TEST(test_lower); 77 | TEST(test_upper); 78 | TEST(test_simple_tokenize1); 79 | TEST_MAIN_REPORT; 80 | } 81 | -------------------------------------------------------------------------------- /src/flpr/Stmt_Parser_Exts.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Stmt_Parser_Exts.cc 13 | */ 14 | 15 | #include "flpr/Stmt_Parser_Exts.hh" 16 | 17 | namespace FLPR { 18 | namespace Stmt { 19 | Parser_Exts &get_parser_exts() noexcept { 20 | static Parser_Exts the_parser_exts; 21 | return the_parser_exts; 22 | } 23 | 24 | void Parser_Exts::register_action_stmt(stmt_parser ext) noexcept { 25 | action_exts_.push_back(ext); 26 | } 27 | 28 | void Parser_Exts::register_other_specification_stmt(stmt_parser ext) noexcept { 29 | other_specification_exts_.push_back(ext); 30 | } 31 | 32 | SP_Result Parser_Exts::parse_action_stmt(TT_Stream &ts) { 33 | auto ts_rewind_point = ts.mark(); 34 | for (auto parser : action_exts_) { 35 | Stmt_Tree st{parser(ts)}; 36 | if (st) { 37 | Stmt_Tree new_root{Syntax_Tags::SG_ACTION_STMT, (*st)->token_range}; 38 | hoist_back(new_root, std::move(st)); 39 | cover_branches(*new_root); 40 | return SP_Result{std::move(new_root), true}; 41 | } 42 | ts.rewind(ts_rewind_point); 43 | } 44 | return SP_Result{Stmt_Tree{}, false}; 45 | } 46 | 47 | SP_Result Parser_Exts::parse_other_specification_stmt(TT_Stream &ts) { 48 | auto ts_rewind_point = ts.mark(); 49 | for (auto parser : other_specification_exts_) { 50 | Stmt_Tree st{parser(ts)}; 51 | if (st) { 52 | Stmt_Tree new_root{Syntax_Tags::SG_OTHER_SPECIFICATION_STMT, 53 | (*st)->token_range}; 54 | hoist_back(new_root, std::move(st)); 55 | cover_branches(*new_root); 56 | return SP_Result{std::move(new_root), true}; 57 | } 58 | ts.rewind(ts_rewind_point); 59 | } 60 | return SP_Result{Stmt_Tree{}, false}; 61 | } 62 | 63 | void Parser_Exts::clear() noexcept { 64 | action_exts_.clear(); 65 | other_specification_exts_.clear(); 66 | } 67 | 68 | } // namespace Stmt 69 | } // namespace FLPR 70 | -------------------------------------------------------------------------------- /src/flpr/Stmt_Tree.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Stmt_Tree.cc 13 | */ 14 | 15 | #include "flpr/Stmt_Tree.hh" 16 | #include 17 | #include 18 | 19 | namespace FLPR { 20 | namespace Stmt { 21 | void cover_branches(Stmt_Tree::reference st) { 22 | // Scan to first non-empty branch 23 | auto b1 = st.branches().begin(); 24 | while (b1 != st.branches().end() && (*b1)->token_range.empty()) 25 | ++b1; 26 | if (b1 == st.branches().end()) 27 | return; // No valid ranges to cover 28 | st->token_range.clear(); 29 | st->token_range.set_it((*b1)->token_range.it()); 30 | st->token_range.push_back((*b1)->token_range); 31 | for (b1 = std::next(b1); b1 != st.branches().end(); ++b1) { 32 | st->token_range.push_back((*b1)->token_range); 33 | } 34 | } 35 | 36 | void hoist_back(Stmt_Tree &t, Stmt_Tree &&donor) { 37 | if (!donor) 38 | return; 39 | if ((*donor)->syntag == Syntax_Tags::HOIST) { 40 | for (auto &&b : donor->branches()) { 41 | auto new_loc = t->branches().emplace(t->branches().end(), std::move(b)); 42 | new_loc->link(new_loc, t.root()); 43 | } 44 | } else { 45 | t.graft_back(std::move(donor)); 46 | } 47 | } 48 | 49 | std::ostream &operator<<(std::ostream &os, ST_Node_Data const &nd) { 50 | return Syntax_Tags::print(os, nd.syntag); 51 | } 52 | 53 | int get_label_do_label(Stmt_Tree const &t) { 54 | auto c = t.ccursor(); 55 | if (FLPR::Syntax_Tags::SG_DO_STMT == c->syntag) 56 | c.down(); 57 | if (FLPR::Syntax_Tags::SG_LABEL_DO_STMT != c->syntag) { 58 | return 0; 59 | } 60 | c.down(); 61 | if (FLPR::Syntax_Tags::TK_NAME == c->syntag) { 62 | // skip the : prefix 63 | c.next().next(); 64 | } 65 | c.next().down(); 66 | assert(FLPR::Syntax_Tags::SG_INT_LITERAL_CONSTANT == c->syntag); 67 | assert(c->token_range.size() == 1); 68 | return std::stoi(c->token_range.front().text()); 69 | } 70 | 71 | } // namespace Stmt 72 | } // namespace FLPR 73 | -------------------------------------------------------------------------------- /src/flpr/Stmt_Tree.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Stmt_Tree.hh 13 | */ 14 | #ifndef FLPR_STMT_TREE_HH 15 | #define FLPR_STMT_TREE_HH 1 16 | 17 | #include "flpr/LL_TT_Range.hh" 18 | #include "flpr/Syntax_Tags.hh" 19 | #include "flpr/Tree.hh" 20 | #include 21 | 22 | namespace FLPR { 23 | namespace Stmt { 24 | 25 | //! The contents of each \c Stmt_Tree node 26 | struct ST_Node_Data { 27 | ST_Node_Data() : syntag{Syntax_Tags::UNKNOWN}, token_range{} {} 28 | explicit ST_Node_Data(int const syntag) : syntag{syntag}, token_range{} {} 29 | ST_Node_Data(int const syntag, LL_TT_Range const &token_range) 30 | : syntag{syntag}, token_range{token_range} {} 31 | ST_Node_Data(int const syntag, LL_TT_Range &&token_range) 32 | : syntag{syntag}, token_range{std::move(token_range)} {} 33 | 34 | //! The Syntax_Tags::Tags associated with this (sub)tree 35 | int syntag; 36 | //! The Logical_Line and range of tokens covered by this (sub)tree 37 | LL_TT_Range token_range; 38 | }; 39 | 40 | std::ostream &operator<<(std::ostream &os, ST_Node_Data const &nd); 41 | 42 | /*! A parse tree/concrete syntax tree for the components of individual 43 | statments. Compare to Pgrm_Tree, which organizes statements into blocks of 44 | various sorts */ 45 | using Stmt_Tree = Tree; 46 | 47 | //! Update the (*st)->token_range to cover the token_ranges of the branches 48 | void cover_branches(Stmt_Tree::reference st); 49 | 50 | //! Treat donor as a list, rather than a tree, in certain circumstances 51 | /*! If the root of the donor tree is marked with Syntax_Tags::HOIST, append the 52 | branches of donor to t.branches(), otherwise t.graft_back(donor) */ 53 | void hoist_back(Stmt_Tree &t, Stmt_Tree &&donor); 54 | 55 | //! Return the label from a label-do-stmt 56 | /*! If t is not a do-stmt->label-do-stmt or label-do-stmt, returns 0 */ 57 | int get_label_do_label(Stmt_Tree const &t); 58 | 59 | } // namespace Stmt 60 | 61 | } // namespace FLPR 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /docs/sphinx/why_flpr.rst: -------------------------------------------------------------------------------- 1 | .. _why_flpr: 2 | 3 | ========= 4 | Why FLPR? 5 | ========= 6 | 7 | A fair question to ask is "why did you develop FLPR when there are 8 | several available open-source Fortran front-ends?" The main reason is 9 | that FLPR is designed for performing complex transformations on 10 | Fortran source code, whereas compiler front-ends are designed to 11 | produce intermediate representations of the code semantics. While 12 | both toolsets require an understanding the syntax of a Fortran source 13 | file, each toolset has a very different view of what is *important* 14 | about that file. For FLPR, the most important thing to capture is the 15 | layout of the text in the file, while a compiler front-end wants to 16 | discard that information as quickly as possible. 17 | 18 | For simple tasks like converting ``real*8`` to ``real(kind=k)``, 19 | generic text transformation tools like ``sed`` and ``awk`` can easily 20 | make changes across a large number of source files. However, consider 21 | the following one-shot modernization task. Suppose your project uses 22 | the convention that uppercased dummy arguments in subroutines implies 23 | that they are output arguments, and lowercase implies that they are 24 | input: 25 | 26 | .. code-block:: fortranfixed 27 | 28 | c fixed-format file 29 | subroutine foo(n,m,S) 30 | integer n,m,S,locali 31 | ... 32 | 33 | You can use FLPR to transform the type declarations into a modern form: 34 | 35 | .. code-block:: fortran 36 | 37 | ! free-format 38 | subroutine foo(n,m,S) 39 | integer, intent(in) :: n,m 40 | integer, intent(out) :: S 41 | integer :: locali 42 | ... 43 | 44 | Doing so requires syntactic information that would be extremely 45 | complicated to encode in terms of the regular expressions required by 46 | generic text transformation tools. 47 | 48 | Another reason for using FLPR over most other tools is that it has an 49 | extensible grammar. You can easily register new *action-stmt* or new 50 | *other-specification-stmt* parsers (extending block-like structures 51 | is on the TO-DO list). With this capability, you can implement 52 | continuous transformation tools that translate application-specific 53 | statements into standard Fortran. Some examples where this might be 54 | useful are: 55 | 56 | 1. Replacing boilerplate code with simple-to-maintain statements; 57 | 2. Testing out potential new standard Fortran language features; 58 | 3. or adapting code to particular platform characteristics. 59 | 60 | -------------------------------------------------------------------------------- /apps/flpr-format.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /* 12 | Generate a free-format Fortran file after applying a set of filters to either 13 | a fixed-format or free-format input file. 14 | 15 | From a FLPR programming perspective, this is a demonstration of augmenting the 16 | Prgm_Tree node data to store client application data. 17 | 18 | The code is split up in an unusual way to allow you to easily use the format 19 | operations with your own FLPR syntax extensions: just copy this file, add your 20 | extensions in main(), and run. 21 | */ 22 | 23 | #include "flpr_format_base.hh" 24 | #include 25 | #include 26 | #include 27 | 28 | int main(int argc, char *const argv[]) { 29 | 30 | /* Read the command-line arguments */ 31 | std::vector filenames; 32 | Options options; 33 | options.enable_all_filters(); 34 | options[OPT(COL72)] = false; 35 | options[OPT(FIXED_TO_FREE)] = false; 36 | options[OPT(REINDENT)] = false; 37 | if (!parse_cmd_line(filenames, options, argc, argv)) { 38 | std::cerr << "exiting on error." << std::endl; 39 | return 1; 40 | } 41 | 42 | /* You could register FLPR syntax extensions here */ 43 | 44 | /* The actual indentation pattern is selected on a file-by-file basis 45 | below. */ 46 | FLPR::Indent_Table indents; 47 | 48 | /* Process each input file */ 49 | for (auto const &fname : filenames) { 50 | File file; 51 | VERBOSE_BEGIN("read_file"); 52 | file.read_file(fname, options[OPT(COL72)]?72:0); 53 | VERBOSE_END; 54 | /* Define the indentation pattern based on the input format. It would be 55 | nice if this was setup from an external configuration file */ 56 | if (file.logical_file().is_fixed_format() && !options[OPT(FIXED_TO_FREE)]) { 57 | indents.apply_constant_fixed_indent(4); 58 | indents.set_continued_offset(5); 59 | } else { 60 | indents.apply_emacs_indent(); 61 | } 62 | if (flpr_format_file(file, options, indents)) { 63 | std::cerr << "Error formating file \"" << fname << "\"" << std::endl; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/flpr/Parser_Result.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Parser_Result.hh 13 | */ 14 | 15 | #ifndef FLPR_PARSER_RESULT_HH 16 | #define FLPR_PARSER_RESULT_HH 1 17 | 18 | #include 19 | 20 | namespace FLPR { 21 | namespace details_ { 22 | //! Utility class for parser results 23 | template class Parser_Result { 24 | public: 25 | using tree_type = TT; 26 | Parser_Result() : parse_tree{}, match{false} {} 27 | Parser_Result(Parser_Result const &) = delete; 28 | Parser_Result(Parser_Result &&) = default; 29 | Parser_Result &operator=(Parser_Result const &) = delete; 30 | Parser_Result &operator=(Parser_Result &&) = default; 31 | Parser_Result(tree_type &&pt, bool match) noexcept 32 | : parse_tree{std::move(pt)}, match{match} {} 33 | operator tree_type() { return std::move(parse_tree); } 34 | 35 | /*! @name result_data The data returned by a parser 36 | * These are public to allow for structured bindings. A parser, such as 37 | * Optional_Parser, may "match" a non-existent term, so we can't rely on the 38 | * state of the tree alone. 39 | */ 40 | //! The (possibly bad) parse tree matched by the parser 41 | tree_type parse_tree; 42 | //! Whether the parser considered this a success or not. 43 | bool match; 44 | }; 45 | 46 | template 47 | std::ostream &operator<<(std::ostream &os, Parser_Result const &r) { 48 | if (r.match) 49 | os << *(r.parse_tree); 50 | else 51 | os << "()"; 52 | return os; 53 | } 54 | 55 | //! Returns true if f(a) is true for all a \in args. 56 | /*! Reduces/folds the application of f to each of args w.r.t. logical AND */ 57 | template 58 | constexpr bool and_fold(F f, Args... args) noexcept { 59 | return (... && f(args)); 60 | } 61 | 62 | //! Returns true if f(a) is true for some a \in args. 63 | /*! Reduces/folds the application of f to each of args w.r.t. logical OR */ 64 | template 65 | constexpr bool or_fold(F f, Args... args) noexcept { 66 | return (... || f(args)); 67 | } 68 | 69 | } // namespace details_ 70 | } // namespace FLPR 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /docs/sphinx/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # http://www.sphinx-doc.org/en/master/config 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | # -- Project information ----------------------------------------------------- 18 | 19 | project = 'FLPR' 20 | copyright = '2019-2020, Triad National Security, LLC' 21 | author = u'Paul Henning' 22 | 23 | # The full version, including alpha/beta/rc tags 24 | release = '0.1.0' 25 | 26 | source_suffix = '.rst' 27 | master_doc = 'index' 28 | 29 | # -- General configuration --------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 44 | 45 | 46 | # -- Options for HTML output ------------------------------------------------- 47 | 48 | # The theme to use for HTML and HTML Help pages. See the documentation for 49 | # a list of builtin themes. 50 | # 51 | 52 | try: 53 | import sphinx_rtd_theme 54 | except: 55 | html_theme = 'classic' 56 | html_theme_options = { 57 | 'codebgcolor': 'lightgrey', 58 | 'stickysidebar': 'true' 59 | } 60 | html_theme_path = [] 61 | else: 62 | html_theme = 'sphinx_rtd_theme' 63 | html_theme_options = {} 64 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 65 | 66 | 67 | # Add any paths that contain custom static files (such as style sheets) here, 68 | # relative to this directory. They are copied after the builtin static files, 69 | # so a file named "default.css" will overwrite the builtin "default.css". 70 | html_static_path = ['_static'] 71 | 72 | html_logo = 'flpr-logo-sm.png' 73 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | # The FLPR To-Do List 2 | 3 | ## File_Line processing 4 | 5 | Items related CPP (C-Preprocessor) and raw line formatting 6 | - Match comment lines to *preceding* Fortran lines if the comment is 7 | approximately aligned with a right-comment on the Fortran stmt. 8 | - Only convert from fixed-format to free-format on request, and make 9 | it distinct from parsing. 10 | - In fixed -> free format conversion, handle comments a bit better: 11 | + If the comment text begins in C2 (column 2), put the comment in 12 | `left_text`. 13 | + If the comment text begins in C7, but there is no fortran 14 | statement in C7 below (e.g. indented), put the comment in 15 | `left_text`. 16 | + If the comment text is aligned with the statement `main_txt`, put 17 | the comment in `main_txt`. 18 | + If the comment text is in `right_text`, leave it there. 19 | - Check that statement continuations are being handled correctly. 20 | + In free format, a leading ampersand on a continuation line implies 21 | that the text of that line gets directly appended to the text of 22 | the previous line before the trailing ampersand. In particular, 23 | *any* lexical token can be split this way. 24 | + In fixed format, characters 7-72 are directly appended to 25 | character 72 of the previous line. This rule is particularly 26 | important in a continued character context. 27 | 28 | ## Parsing 29 | 30 | Implement the missing parsers: 31 | * *block-data* 32 | * *change-team-construct* 33 | * *critical-construct* 34 | * *submodule* 35 | 36 | ## Efficiency 37 | 38 | Items related to performance and memory use. 39 | - Speed up the parser combinator approach. 40 | - Look at clearing `main_txt` from `File_Line` once it has been put 41 | under the control of a `Logical_Line` (text is already found in the 42 | `Token_Text`) 43 | - Could "collapse" linear subtrees in the `Stmt_Tree`. 44 | - Consider reading/processing/writing one program-unit at a time, 45 | rather than reading in the entire file. 46 | - Add in switch statements for things like action-stmt to jump to appropriate 47 | parsers, rather than sequentially moving through them. 48 | 49 | 50 | ## Build System, Directory Structure, Testing 51 | 52 | Items related to CMake configuration, the layout of directories and 53 | files, and the test system 54 | (preprocessor and library). 55 | - Integrate the documentation systems 56 | + Add breathe and exhale support to Sphinx 57 | + Update doxygen output to work with above 58 | + Make it all work with CMake 59 | - Simplify the FLPRApp install. 60 | - Add more tests. Always add more tests. 61 | 62 | -------------------------------------------------------------------------------- /src/flpr/Syntax_Tags.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file include/flpr/Syntax_Tags.hh 13 | Definition of FLPR::Syntax_Tags, used to identify nodes in a 14 | parse tree. 15 | */ 16 | 17 | #ifndef FLPR_SYNTAX_TAGS_HH 18 | #define FLPR_SYNTAX_TAGS_HH 1 19 | 20 | #include "flpr/Syntax_Tags_Defs.hh" 21 | #include 22 | #include 23 | #include 24 | 25 | namespace FLPR { 26 | 27 | //! Define a class to hold the enums representing the syntax tags 28 | /*! We aren't using an enum class here because we want to store the tags in the 29 | tree as an integer, so that the client can extend the tag values. */ 30 | struct Syntax_Tags { 31 | enum Tags { MAP(LISTIZE) }; 32 | 33 | public: 34 | static std::string label(int const syntag); 35 | static constexpr int pg_begin_tag() { return PG_000_LB + 1; } 36 | static constexpr int pg_end_tag() { return PG_ZZZ_UB; } 37 | static constexpr bool is_name(int const tag) { 38 | return tag == TK_NAME || types_[tag] == 4; 39 | } 40 | static int type(int const syntag); 41 | 42 | static std::ostream &print(std::ostream &os, int const syntag) { 43 | return os << label(syntag); 44 | } 45 | static bool is_keyword(int const syntag) { return type(syntag) == 4; } 46 | static bool register_ext(int const tag_idx, char const *const label, 47 | int const type); 48 | 49 | public: 50 | struct Ext_Record { 51 | std::string label; 52 | int type{-1}; 53 | constexpr bool empty() const { return type == -1; } 54 | }; 55 | 56 | private: 57 | static constexpr char const *const strings_[] = {MAP(STRINGIZE)}; 58 | static constexpr int types_[] = {MAP(TYPIZE)}; 59 | static std::vector extensions_; 60 | static int get_ext_idx_(int const syntag) { 61 | if (syntag < CLIENT_EXTENSION) 62 | return -2; 63 | const int ext_idx = syntag - CLIENT_EXTENSION; 64 | if (ext_idx >= static_cast(extensions_.size()) || 65 | extensions_[ext_idx].empty()) 66 | return -1; 67 | return ext_idx; 68 | } 69 | }; 70 | 71 | } // namespace FLPR 72 | 73 | #undef MAP 74 | #undef LISTIZE 75 | #undef STRINGIZE 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/flpr/LL_Stmt_Src.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file LL_Stmt_Src.cc 13 | */ 14 | 15 | #include "flpr/LL_Stmt_Src.hh" 16 | 17 | namespace FLPR { 18 | void LL_Stmt_Src::refill_() { 19 | const bool make_macro_stmts{false}; 20 | LL_Stmt::LL_IT_SEQ prefix_lines; 21 | buf_.clear(); 22 | 23 | // Advance through non-statement prefix lines 24 | while (it_ && 25 | (!make_macro_stmts || it_->cat != LineCat::MACRO || it_->suppress) && 26 | it_->stmts().empty()) { 27 | assert(it_->stmts().empty()); 28 | if (!it_->suppress) 29 | prefix_lines.push_back(it_); 30 | it_.advance(); 31 | } 32 | 33 | // Now at a non-trivial LL, a Macro, OR the end of the input 34 | if (it_) { 35 | assert(!it_->suppress); 36 | if (it_->cat == LineCat::MACRO) { 37 | // This will create an "empty()" LL_Stmt with prefix_lines 38 | // with preceding trivial LL and the MACRO in prefix_lines.back(). 39 | prefix_lines.push_back(it_); 40 | buf_.emplace_back(); 41 | buf_.back().prefix_lines.swap(prefix_lines); 42 | } else { 43 | int label = it_->label; 44 | int compound = (it_->stmts().size() > 1) ? 1 : 0; 45 | 46 | for (auto &srange : it_->stmts()) { 47 | buf_.emplace_back(it_, srange, label, compound); 48 | compound += 1; 49 | // Assign any trivial prefix to the first statement of 50 | // a compound 51 | if (!prefix_lines.empty()) 52 | buf_.back().prefix_lines.swap(prefix_lines); 53 | // Clear the label, as it only applies to the first statement 54 | label = 0; 55 | } 56 | } 57 | it_.advance(); 58 | } else // at end of input 59 | { 60 | if (!prefix_lines.empty()) { 61 | // This used to create an "empty()" LL_Stmt with prefix_lines 62 | // containing the end-of-file comments, but that really isn't a statement. 63 | // buf_.emplace_back(); 64 | // buf_.back().prefix_lines.swap(prefix_lines); 65 | } 66 | } 67 | 68 | curr_ = buf_.begin(); 69 | } 70 | 71 | #if 0 72 | void LL_Stmt_Src::push_front(value_type const &s) { buf_.insert(curr_, s); } 73 | #endif 74 | 75 | } // namespace FLPR 76 | -------------------------------------------------------------------------------- /tests/test_label_stack.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /* 12 | Testing for the Label_Stack class 13 | */ 14 | #include "flpr/Label_Stack.hh" 15 | #include "test_helpers.hh" 16 | 17 | using FLPR::Label_Stack; 18 | 19 | bool empty_stack() { 20 | Label_Stack ls; 21 | TEST_TRUE(ls.empty()); 22 | TEST_INT(ls.size(), 0); 23 | TEST_INT(ls.level(999), -1); 24 | TEST_FALSE(ls.is_top(999)); 25 | return true; 26 | } 27 | 28 | bool one_entry() { 29 | Label_Stack ls; 30 | ls.push(999); 31 | TEST_FALSE(ls.empty()); 32 | TEST_INT(ls.size(), 1); 33 | TEST_INT(ls.level(555), -1); 34 | TEST_INT(ls.level(999), 0); 35 | TEST_TRUE(ls.is_top(999)); 36 | ls.pop(); 37 | TEST_TRUE(ls.empty()); 38 | TEST_INT(ls.size(), 0); 39 | TEST_INT(ls.level(999), -1); 40 | TEST_FALSE(ls.is_top(999)); 41 | return true; 42 | } 43 | 44 | bool two_diff_entry() { 45 | Label_Stack ls; 46 | ls.push(1); 47 | ls.push(2); 48 | TEST_FALSE(ls.empty()); 49 | TEST_INT(ls.size(), 2); 50 | TEST_INT(ls.level(1), -1); 51 | TEST_INT(ls.level(2), 0); 52 | ls.pop(); 53 | TEST_INT(ls.size(), 1); 54 | TEST_INT(ls.level(1), 0); 55 | return true; 56 | } 57 | 58 | bool two_same_entry() { 59 | Label_Stack ls; 60 | ls.push(1); 61 | ls.push(1); 62 | TEST_INT(ls.level(2), -1); 63 | TEST_INT(ls.level(1), 2); 64 | ls.pop(); 65 | TEST_INT(ls.level(1), 1); 66 | return true; 67 | } 68 | 69 | bool seq1() { 70 | Label_Stack ls; 71 | ls.push(2); 72 | ls.push(2); 73 | ls.push(1); 74 | ls.push(1); 75 | ls.push(1); 76 | ls.push(2); 77 | TEST_INT(ls.level(2), 0); 78 | ls.pop(); 79 | TEST_INT(ls.level(2), -1); 80 | TEST_INT(ls.level(1), 3); 81 | ls.pop(); 82 | TEST_INT(ls.level(1), 2); 83 | ls.pop(); 84 | TEST_INT(ls.level(1), 1); 85 | ls.pop(); 86 | TEST_INT(ls.level(2), 2); 87 | ls.pop(); 88 | TEST_INT(ls.level(2), 1); 89 | return true; 90 | } 91 | 92 | int main() { 93 | TEST_MAIN_DECL; 94 | 95 | TEST(empty_stack); 96 | TEST(one_entry); 97 | TEST(two_diff_entry); 98 | TEST(two_same_entry); 99 | TEST(seq1); 100 | 101 | TEST_MAIN_REPORT; 102 | } 103 | -------------------------------------------------------------------------------- /src/flpr/Indent_Table.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Indent_Table.cc 13 | 14 | Manage indent spacing for Prgm Grammar (PG_) syntax tags. 15 | */ 16 | 17 | #include "flpr/Indent_Table.hh" 18 | 19 | namespace FLPR { 20 | 21 | void Indent_Table::apply_constant_indent(int const spaces) { 22 | set_indent(Syntax_Tags::PG_BLOCK, spaces); 23 | set_indent(Syntax_Tags::PG_EXECUTION_PART, spaces); 24 | set_indent(Syntax_Tags::PG_INTERFACE_SPECIFICATION, spaces); 25 | set_indent(Syntax_Tags::PG_INTERNAL_SUBPROGRAM, spaces); 26 | set_indent(Syntax_Tags::PG_MODULE_SUBPROGRAM, spaces); 27 | set_indent(Syntax_Tags::PG_SPECIFICATION_PART, spaces); 28 | set_indent(Syntax_Tags::PG_WHERE_BODY_CONSTRUCT, spaces); 29 | 30 | for (int i = Syntax_Tags::PG_000_LB + 1; i < Syntax_Tags::PG_ZZZ_UB; ++i) 31 | if (begin_end_construct(i)) 32 | set_indent(i, spaces); 33 | set_continued_offset(5); 34 | } 35 | 36 | void Indent_Table::apply_emacs_indent() { 37 | set_indent(Syntax_Tags::PG_BLOCK, 3); 38 | set_indent(Syntax_Tags::PG_EXECUTION_PART, 2); 39 | set_indent(Syntax_Tags::PG_INTERFACE_SPECIFICATION, 3); 40 | set_indent(Syntax_Tags::PG_INTERNAL_SUBPROGRAM, 2); 41 | set_indent(Syntax_Tags::PG_MODULE_SUBPROGRAM, 2); 42 | set_indent(Syntax_Tags::PG_SPECIFICATION_PART, 2); 43 | set_indent(Syntax_Tags::PG_WHERE_BODY_CONSTRUCT, 3); 44 | 45 | for (int i = Syntax_Tags::PG_000_LB + 1; i < Syntax_Tags::PG_ZZZ_UB; ++i) 46 | if (begin_end_construct(i)) 47 | set_indent(i, 3); 48 | set_continued_offset(5); 49 | } 50 | 51 | void Indent_Table::apply_constant_fixed_indent(int const spaces) { 52 | set_indent(Syntax_Tags::PG_BLOCK, spaces); 53 | set_indent(Syntax_Tags::PG_EXECUTION_PART, 0); 54 | set_indent(Syntax_Tags::PG_INTERFACE_SPECIFICATION, spaces); 55 | set_indent(Syntax_Tags::PG_INTERNAL_SUBPROGRAM, spaces); 56 | set_indent(Syntax_Tags::PG_MODULE_SUBPROGRAM, spaces); 57 | set_indent(Syntax_Tags::PG_SPECIFICATION_PART, 0); 58 | set_indent(Syntax_Tags::PG_WHERE_BODY_CONSTRUCT, spaces); 59 | 60 | for (int i = Syntax_Tags::PG_000_LB + 1; i < Syntax_Tags::PG_ZZZ_UB; ++i) 61 | if (begin_end_construct(i)) 62 | set_indent(i, spaces); 63 | set_continued_offset(5); 64 | } 65 | 66 | } // namespace FLPR 67 | -------------------------------------------------------------------------------- /apps/module_base.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file module_base.hh 13 | 14 | Demonstrating how to selectively insert a *use-stmt* into 15 | subprograms that contain a *call-stmt* to a particular name. You 16 | may want functionality like this when moving old code into modules. 17 | */ 18 | 19 | #ifndef MODULE_BASE_HH 20 | #define MODULE_BASE_HH 1 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace FLPR_Module { 28 | 29 | using File = FLPR::Parsed_File<>; 30 | using Cursor = typename File::Parse_Tree::cursor_t; 31 | using Stmt_Cursor = typename File::Stmt_Cursor; 32 | using Stmt_Range = typename File::Parse_Tree::value::Stmt_Range; 33 | using Stmt_Iter = typename Stmt_Range::iterator; 34 | using Stmt_Const_Iter = typename Stmt_Range::const_iterator; 35 | using Procedure = FLPR::Procedure; 36 | 37 | class Module_Action { 38 | public: 39 | Module_Action(std::string &&module_name, 40 | std::vector &&only_names) 41 | : module_name_{std::move(module_name)}, only_names_{ 42 | std::move(only_names)} { 43 | module_lc_ = module_name_; 44 | FLPR::tolower(module_lc_); 45 | assert(only_names_.empty()); 46 | } 47 | void add_subroutine_name(std::string name) { 48 | FLPR::tolower(name); 49 | subroutine_names_.emplace(std::move(name)); 50 | } 51 | 52 | bool operator()(File &file, Cursor c, bool const internal_procedure, 53 | bool const module_procedure) const; 54 | 55 | private: 56 | std::string module_name_; 57 | std::vector only_names_; 58 | std::unordered_set subroutine_names_; 59 | std::string module_lc_; 60 | }; 61 | 62 | bool do_file(std::string const &filename, int const last_fixed_col, 63 | FLPR::File_Type file_type, Module_Action const &action); 64 | void write_file(std::ostream &os, File const &f); 65 | bool has_call_named(FLPR::LL_Stmt const &stmt, 66 | std::unordered_set const &lowercase_names); 67 | 68 | Stmt_Cursor find_use_module_name(FLPR::LL_Stmt &stmt); 69 | FLPR::File_Type file_type_from_ext(std::string const &filename); 70 | 71 | } // namespace FLPR_Module 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/flpr/utils.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file utils.hh 13 | */ 14 | 15 | #ifndef FLPR_UTILS_HH 16 | #define FLPR_UTILS_HH 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace FLPR { 25 | //! convert the given string to lower case 26 | void tolower(std::string &); 27 | 28 | //! convert the given string to upper case 29 | void toupper(std::string &); 30 | 31 | //! break the string s by the delimiter character 32 | /*! Note that this skips repeated delimiters */ 33 | void simple_tokenize(std::string const &s, std::vector &t, 34 | const std::string::value_type delim = ' '); 35 | 36 | //! remove whitespace from the begining and end of the string 37 | inline std::string trim(const std::string &s) { 38 | const auto wsfront = std::find_if_not(s.begin(), s.end(), 39 | [](char c) { return std::isspace(c); }); 40 | const auto wsback = 41 | std::find_if_not(s.rbegin(), std::string::const_reverse_iterator(wsfront), 42 | [](char c) { return std::isspace(c); }) 43 | .base(); 44 | 45 | return std::string(wsfront, wsback); 46 | } 47 | 48 | //! remove whitespace from the end of the string (copy) 49 | inline std::string trim_back_copy(std::string const &s) { 50 | std::string::size_type back = s.find_last_not_of(" \t"); 51 | if (back == std::string::npos) 52 | return std::string(); 53 | return std::string(s, 0, back + 1); 54 | } 55 | 56 | //! remove whitespace from the end of the string (in-place) 57 | inline void trim_back(std::string &s) { 58 | std::string::size_type back = s.find_last_not_of(" \t"); 59 | if (back == std::string::npos) 60 | s.clear(); 61 | else 62 | s.erase(back + 1); 63 | } 64 | 65 | //! return the last non-blank character in s, or '\0' 66 | inline char last_non_blank_char(std::string const &s) { 67 | std::string::size_type back = s.find_last_not_of(" \t"); 68 | if (back == std::string::npos) 69 | return '\0'; 70 | return s[back]; 71 | } 72 | 73 | //! remove the capacity from a vector 74 | template inline void destroy(std::vector &v) { 75 | std::vector empty_vec; 76 | empty_vec.swap(v); 77 | } 78 | 79 | void print_ctrlchars(std::ostream &os, const std::string &s); 80 | } // namespace FLPR 81 | #endif 82 | -------------------------------------------------------------------------------- /docs/Smash_Hash.gperf: -------------------------------------------------------------------------------- 1 | %language=C++ 2 | %struct-type 3 | %{ 4 | /* 5 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 6 | 7 | This is open source software; you can redistribute it and/or modify it 8 | under the terms of the BSD-3 License. If software is modified to produce 9 | derivative works, such modified software should be clearly marked, so as 10 | not to confuse it with the version available from LANL. Full text of the 11 | BSD-3 License can be found in the LICENSE file of the repository. 12 | */ 13 | 14 | /*! 15 | \file Smash_Hash.hh 16 | Perfect hash function developed to split names that are actually 17 | adjacent keywords without a space, as per section 6.3.2.2. This file 18 | was generated by GNU gperf 3.1 using the input found in 19 | $FLPR/docs/Smash_Hash.gperf. 20 | 21 | NOTE: doubleprecision is NOT included in this! The parsers handle it 22 | directly by combining DOUBLE PRECISION under a KW_DOUBLEPRECISION 23 | root. 24 | */ 25 | #ifndef SMASH_HASH_HH 26 | #define SMASH_HASH_HH 1 27 | 28 | #include 29 | #include 30 | namespace FLPR { 31 | namespace details_ { 32 | %} 33 | struct Smashed { char const * name; int tok1; int tok2; int splitpos; }; 34 | %% 35 | blockdata, Syntax_Tags::KW_BLOCK, Syntax_Tags::KW_DATA, 5 36 | elseif, Syntax_Tags::KW_ELSE, Syntax_Tags::KW_IF, 4 37 | elsewhere, Syntax_Tags::KW_ELSE, Syntax_Tags::KW_WHERE, 4 38 | endassociate, Syntax_Tags::KW_END, Syntax_Tags::KW_ASSOCIATE, 3 39 | endblock, Syntax_Tags::KW_END, Syntax_Tags::KW_BLOCK, 3 40 | endcritical, Syntax_Tags::KW_END, Syntax_Tags::KW_CRITICAL, 3 41 | enddo, Syntax_Tags::KW_END, Syntax_Tags::KW_DO, 3 42 | endenum, Syntax_Tags::KW_END, Syntax_Tags::KW_ENUM, 3 43 | endfile, Syntax_Tags::KW_END, Syntax_Tags::KW_FILE, 3 44 | endforall, Syntax_Tags::KW_END, Syntax_Tags::KW_FORALL, 3 45 | endfunction, Syntax_Tags::KW_END, Syntax_Tags::KW_FUNCTION, 3 46 | endif, Syntax_Tags::KW_END, Syntax_Tags::KW_IF, 3 47 | endinterface, Syntax_Tags::KW_END, Syntax_Tags::KW_INTERFACE, 3 48 | endmodule, Syntax_Tags::KW_END, Syntax_Tags::KW_MODULE, 3 49 | endprocedure, Syntax_Tags::KW_END, Syntax_Tags::KW_PROCEDURE, 3 50 | endprogram, Syntax_Tags::KW_END, Syntax_Tags::KW_PROGRAM, 3 51 | endselect, Syntax_Tags::KW_END, Syntax_Tags::KW_SELECT, 3 52 | endsubmodule, Syntax_Tags::KW_END, Syntax_Tags::KW_SUBMODULE, 3 53 | endsubroutine, Syntax_Tags::KW_END, Syntax_Tags::KW_SUBROUTINE, 3 54 | endteam, Syntax_Tags::KW_END, Syntax_Tags::KW_TEAM, 3 55 | endtype, Syntax_Tags::KW_END, Syntax_Tags::KW_TYPE, 3 56 | endwhere, Syntax_Tags::KW_END, Syntax_Tags::KW_WHERE, 3 57 | goto, Syntax_Tags::KW_GO, Syntax_Tags::KW_TO, 2 58 | selectcase, Syntax_Tags::KW_SELECT, Syntax_Tags::KW_CASE, 6 59 | selecttype, Syntax_Tags::KW_SELECT, Syntax_Tags::KW_TYPE, 6 60 | %% 61 | } // details_ 62 | } // FLPR 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/flpr/Line_Accum.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Line_Accum.hh 13 | */ 14 | 15 | #ifndef FLPR_LINE_ACCUM_HH 16 | #define FLPR_LINE_ACCUM_HH 1 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace FLPR { 23 | //! A helper class for determining token offsets 24 | /*! For a given Logical_Line, the flex scanner is given a string that is the 25 | concatenation of the main_txt field of File_Lines. Flex reports the starting 26 | position of tokens as an offset into its input string. This class accumulates 27 | the string for the scanner, and provides translations from the flex starting 28 | location back to file and layout_ line and column numbers. */ 29 | class Line_Accum { 30 | public: 31 | //! Add a main_txt string to the accumulator. 32 | void add_line(int const file_lineno, int const num_left_spaces, 33 | int const main_txt_file_colno, std::string const &main_txt, 34 | int const num_right_spaces); 35 | 36 | //! Return the file line and column 37 | /*! 38 | \param[in] offset The offset returned by yylex 39 | \param[out] lineno The (index 1) line number in the input file 40 | \param[out] colno The (index 1) column number in the input file 41 | */ 42 | bool linecolno(int accum_offset, int &lineno, int &colno) const; 43 | 44 | //! Return the file line and column, and offsets into main_txt 45 | /*! 46 | \param[in] offset The offset returned by yylex 47 | \param[out] lineno The (index 1) line number in the input file 48 | \param[out] colno The (index 1) column number in the input file 49 | \param[out] txt_lineno The (index 0) main_txt line number 50 | \param[out] txt_colno The (index 0) main_txt column number 51 | */ 52 | bool linecolno(int accum_offset, int &lineno, int &colno, int &txt_lineno, 53 | int &txt_colno) const; 54 | 55 | std::string const &accum() const { return accum_; } 56 | std::ostream &print(std::ostream &os) const; 57 | 58 | private: 59 | std::string accum_; 60 | /* lli means "local line index", and it is the (index 0) line number into the 61 | implicit list of File_Lines that are added to this accumulator. */ 62 | std::vector lli_to_accum_offset_; 63 | std::vector lli_to_file_line_num_; 64 | std::vector lli_to_file_column_num_; 65 | }; 66 | } // namespace FLPR 67 | #endif 68 | -------------------------------------------------------------------------------- /src/flpr/LL_TT_Range.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file LL_TT_Range.hh 13 | */ 14 | 15 | #ifndef FLPR_LL_TT_RANGE_HH 16 | #define FLPR_LL_TT_RANGE_HH 1 17 | 18 | #include "flpr/Logical_Line.hh" 19 | #include "flpr/Token_Text.hh" 20 | 21 | namespace FLPR { 22 | //! Identify a range of elements in the fragments of a particular Logical_Line 23 | class LL_TT_Range : public TT_Range { 24 | public: 25 | using LL_IT = LL_SEQ::iterator; 26 | 27 | LL_TT_Range() : TT_Range(), ll_set_{false} {} 28 | LL_TT_Range(LL_TT_Range const &) = default; 29 | LL_TT_Range(LL_TT_Range &&) = default; 30 | LL_TT_Range &operator=(LL_TT_Range const &) = default; 31 | LL_TT_Range &operator=(LL_TT_Range &&) = default; 32 | 33 | LL_TT_Range(LL_IT const &line_ref, TT_Range const &r) 34 | : TT_Range{r}, line_ref_{line_ref}, ll_set_{true} {} 35 | LL_TT_Range(LL_IT const &line_ref, TT_Range::iterator const &beg, 36 | TT_Range::iterator const &end) 37 | : TT_Range{beg, end}, line_ref_{line_ref}, ll_set_{true} {} 38 | 39 | //! Access the iterator to the owning Logical_Line 40 | LL_IT it() const { 41 | assert(ll_set_); 42 | return line_ref_; 43 | } 44 | 45 | //! Access the owning Logical_Line 46 | Logical_Line &ll() { return *it(); } 47 | Logical_Line const &ll() const { 48 | assert(ll_set_); 49 | return *it(); 50 | } 51 | 52 | //! Update the owning Logical_Line iterator 53 | /*! Used to move this TT_Range of tokens to a new LL */ 54 | void set_it(LL_IT const it) { 55 | line_ref_ = it; 56 | ll_set_ = true; 57 | } 58 | 59 | //! Return true if in a multi-statement Logical_Line 60 | bool in_compound() const noexcept { return ll().is_compound(); } 61 | 62 | //! Return the line number associated with the first token 63 | constexpr int linenum() const { 64 | if (empty()) 65 | return -1; 66 | return front().start_line; 67 | } 68 | 69 | //! Return the column number associated with the first token 70 | constexpr int colnum() const { 71 | if (empty()) 72 | return -1; 73 | return front().start_pos; 74 | } 75 | 76 | constexpr bool equal(LL_TT_Range const &rhs) const { 77 | return TT_Range::equal(rhs) && line_ref_ == rhs.line_ref_; 78 | } 79 | 80 | virtual std::ostream &print(std::ostream &os) const; 81 | 82 | protected: 83 | LL_IT line_ref_; 84 | 85 | private: 86 | bool ll_set_; 87 | }; 88 | } // namespace FLPR 89 | #endif 90 | -------------------------------------------------------------------------------- /src/flpr/Indent_Table.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Indent_Table.hh 13 | 14 | Manage indent spacing for Prgm Grammar (PG_) syntax tags. 15 | */ 16 | 17 | #ifndef INDENT_TABLE_HH 18 | #define INDENT_TABLE_HH 1 19 | 20 | #include "flpr/Syntax_Tags.hh" 21 | #include 22 | #include 23 | 24 | namespace FLPR { 25 | class Indent_Table { 26 | public: 27 | Indent_Table() : continued_{2} { offset_.fill(0); } 28 | void apply_constant_indent(int const spaces); 29 | void apply_constant_fixed_indent(int const spaces); 30 | void apply_emacs_indent(); 31 | constexpr static bool begin_end_construct(int const syntag); 32 | constexpr void set_indent(int const syntag, int const spaces) noexcept { 33 | spaces_(syntag) = spaces; 34 | } 35 | constexpr int get_indent(int const syntag) const noexcept { 36 | return spaces_(syntag); 37 | } 38 | constexpr int operator[](int const syntag) const noexcept { 39 | return spaces_(syntag); 40 | } 41 | constexpr void set_continued_offset(int const spaces) noexcept { 42 | continued_ = spaces; 43 | } 44 | constexpr int continued_offset() const noexcept { return continued_; } 45 | 46 | private: 47 | /* Find the extent of the Program Grammar syntax tags */ 48 | constexpr static int PG_BEGIN = Syntax_Tags::pg_begin_tag(); 49 | constexpr static int PG_END = Syntax_Tags::pg_end_tag(); 50 | constexpr static int PG_COUNT = PG_END - PG_BEGIN; 51 | 52 | /* Storage for indent spaces offsets by PG syntag */ 53 | std::array offset_; 54 | /* Additional spacing for continued lines */ 55 | int continued_; 56 | 57 | private: 58 | /* Accessors that perform map from syntax tag to offset_ index */ 59 | constexpr int &spaces_(int const syntag) noexcept { 60 | assert(PG_BEGIN <= syntag && PG_END > syntag); 61 | return offset_[syntag - PG_BEGIN]; 62 | } 63 | constexpr int spaces_(int const syntag) const noexcept { 64 | assert(PG_BEGIN <= syntag && PG_END > syntag); 65 | return offset_[syntag - PG_BEGIN]; 66 | } 67 | }; 68 | 69 | // You don't need to add ones where begin/end just surround a block 70 | constexpr bool Indent_Table::begin_end_construct(int const syntag) { 71 | switch (syntag) { 72 | case Syntax_Tags::PG_DERIVED_TYPE_DEF: 73 | case Syntax_Tags::PG_ENUM_DEF: 74 | case Syntax_Tags::PG_CASE_CONSTRUCT: 75 | return true; 76 | } 77 | return false; 78 | } 79 | } // namespace FLPR 80 | #endif 81 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # FLPR/apps/CMakeLists.txt 11 | 12 | 13 | add_library(flprapp 14 | flpr_format_base.cc 15 | module_base.cc 16 | ) 17 | 18 | target_compile_features(flprapp PUBLIC cxx_std_17) 19 | set_target_properties(flprapp PROPERTIES CXX_EXTENSIONS OFF) 20 | target_link_libraries(flprapp flpr) 21 | target_include_directories(flprapp 22 | PUBLIC 23 | $ 24 | $ 25 | $ 26 | ) 27 | 28 | # Install the application library header files 29 | set(APP_HEADERS 30 | "flpr_format_base.hh" 31 | "module_base.hh" 32 | "Timer.hh" 33 | ) 34 | 35 | # Add any demo applications to this list 36 | set(APPS_EXE 37 | "caliper" 38 | "flpr-format" 39 | "parse_files" 40 | "module" 41 | "ext_demo" 42 | "flpr_show_cst") 43 | 44 | # Installation Info 45 | include(GNUInstallDirs) 46 | 47 | 48 | # Generate an executable target for each entry in APPS_EXE 49 | foreach(e IN LISTS APPS_EXE) 50 | add_executable("${e}" "${e}.cc") 51 | target_link_libraries(${e} flpr flprapp) 52 | target_compile_features(${e} PUBLIC cxx_std_17) 53 | set_target_properties(${e} PROPERTIES CXX_EXTENSIONS OFF) 54 | install(TARGETS "${e}" DESTINATION ${CMAKE_INSTALL_BINDIR}) 55 | endforeach(e) 56 | 57 | 58 | 59 | install(TARGETS flprapp 60 | EXPORT FLPRAppTargets 61 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 62 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 63 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 64 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 65 | ) 66 | 67 | 68 | install(FILES ${APP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/flpr) 69 | 70 | 71 | # Write out config-file package information 72 | 73 | include(CMakePackageConfigHelpers) 74 | 75 | write_basic_package_version_file( 76 | ${CMAKE_CURRENT_BINARY_DIR}/FLPRAppConfigVersion.cmake 77 | VERSION ${PROJECT_VERSION} 78 | COMPATIBILITY SameMajorVersion ) 79 | 80 | export(EXPORT FLPRAppTargets 81 | FILE ${CMAKE_CURRENT_BINARY_DIR}/FLPRAppTargets.cmake 82 | ) 83 | 84 | configure_package_config_file(FLPRAppConfig.cmake.in 85 | ${CMAKE_CURRENT_BINARY_DIR}/FLPRAppConfig.cmake 86 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake 87 | PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR) 88 | 89 | install(EXPORT FLPRAppTargets 90 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake 91 | ) 92 | 93 | 94 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FLPRAppConfig.cmake 95 | ${CMAKE_CURRENT_BINARY_DIR}/FLPRAppConfigVersion.cmake 96 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake ) 97 | -------------------------------------------------------------------------------- /src/flpr/Label_Stack.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Label_Stack.hh 13 | */ 14 | #ifndef FLPR_LABEL_STACK_HH 15 | #define FLPR_LABEL_STACK_HH 1 16 | 17 | #include 18 | #include 19 | 20 | namespace FLPR { 21 | 22 | class Label_Stack { 23 | public: 24 | //! add a new label to the top of the stack 25 | void push(int const label) { label_stack_.emplace_back(Label_Rec_(label)); } 26 | 27 | //! remove the entry at the top of the stack 28 | void pop() { label_stack_.pop_back(); } 29 | 30 | //! Return true if label matches the top stack entry 31 | bool is_top(int const label) const { 32 | return (!label_stack_.empty() && label_stack_.back().label == label); 33 | } 34 | 35 | //! if label matches the top stack entry, return the level, else -1 36 | inline int level(int const label); 37 | 38 | //! return true if empty 39 | bool empty() const { return label_stack_.empty(); } 40 | 41 | //! return the number of elements in the stack 42 | size_t size() const { return label_stack_.size(); } 43 | 44 | private: 45 | /* The meaning of the "level" field 46 | -1 : levels not yet computed 47 | 0 : 1 of 1 of this label 48 | 1 : 1 of N of this label (outer-most) 49 | ... 50 | N : N of N of this label (inner-most) 51 | */ 52 | struct Label_Rec_ { 53 | public: 54 | Label_Rec_() = default; 55 | explicit Label_Rec_(int const l) : label(l), level(-1) {} 56 | 57 | public: 58 | int label, level; 59 | }; 60 | using Stack_T = std::vector; 61 | Stack_T label_stack_; 62 | }; 63 | 64 | inline int Label_Stack::level(int const label) { 65 | /* If the stack is empty or label doesn't match the top entry, return -1 */ 66 | if (label_stack_.empty()) 67 | return -1; 68 | if (label_stack_.back().label != label) 69 | return -1; 70 | 71 | /* If the levels have not been assigned, do so */ 72 | if (label_stack_.back().level == -1) { 73 | Stack_T::reverse_iterator it = label_stack_.rbegin(); 74 | while (it != label_stack_.rend() && it->label == label) 75 | std::advance(it, 1); 76 | auto count = std::distance(label_stack_.rbegin(), it); 77 | assert(count > 0); 78 | if (count == 1) { 79 | /* If there is only one of this label at the top of the stack, it 80 | gets a level == 0 */ 81 | std::advance(it, -1); 82 | it->level = 0; 83 | } else { 84 | /* If there are more than one of this label in a sequence at the top of 85 | the stack, they are assigned a label of "i" (of count). For example, 86 | if count == 2, the top of the stack entry will get a level of two, and 87 | the second entry on the stack will get a level of one. */ 88 | for (int i = 0; i < count; ++i) { 89 | std::advance(it, -1); 90 | it->level = i + 1; 91 | } 92 | } 93 | } 94 | 95 | return label_stack_.back().level; 96 | } 97 | 98 | } // namespace FLPR 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/flpr/LL_Stmt.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file LL_Stmt.cc 13 | */ 14 | 15 | #include "flpr/LL_Stmt.hh" 16 | #include "flpr/parse_stmt.hh" 17 | #include 18 | 19 | #define DEBUG_PRINT 0 20 | 21 | #if DEBUG_PRINT 22 | #include 23 | #endif 24 | 25 | namespace FLPR { 26 | 27 | bool LL_Stmt::set_leading_spaces(int const spaces, int const continued_offset) { 28 | assert(spaces >= 0); 29 | bool changed = false; 30 | 31 | if (is_compound() < 2) { 32 | for (FLPR::LL_SEQ::iterator ll_it : prefix_lines) { 33 | changed |= ll_it->set_leading_spaces(spaces, continued_offset); 34 | } 35 | changed |= ll().set_leading_spaces(spaces, continued_offset); 36 | } 37 | return changed; 38 | } 39 | 40 | int LL_Stmt::stmt_tag(bool const look_inside_if_stmt) const { 41 | int retval; 42 | if (look_inside_if_stmt && Syntax_Tags::SG_IF_STMT == stmt_syntag_ && 43 | !stmt_tree().empty()) { 44 | auto cursor = stmt_tree_.ccursor(); 45 | assert(Syntax_Tags::SG_ACTION_STMT == cursor->syntag); 46 | cursor.down(); 47 | assert(Syntax_Tags::SG_IF_STMT == cursor->syntag); 48 | cursor.down(); 49 | assert(Syntax_Tags::KW_IF == cursor->syntag); 50 | cursor.next(4); 51 | assert(Syntax_Tags::SG_ACTION_STMT == cursor->syntag); 52 | cursor.down(); 53 | retval = -cursor->syntag; 54 | } else { 55 | retval = stmt_syntag_; 56 | } 57 | return retval; 58 | } 59 | 60 | bool LL_Stmt::rebuild_tree_() const { 61 | if (Syntax_Tags::UNKNOWN == stmt_syntag_) { 62 | #if DEBUG_PRINT 63 | std::cerr << "UNKNOWN stmt_syntag on "; 64 | print_me(std::cerr, false) << '\n'; 65 | #endif 66 | return false; 67 | } 68 | TT_Stream tts{*const_cast(this)}; 69 | if (Stmt::is_action_stmt(stmt_syntag_)) { 70 | stmt_tree_ = Stmt::parse_stmt_dispatch(Syntax_Tags::SG_ACTION_STMT, tts); 71 | } else { 72 | stmt_tree_ = Stmt::parse_stmt_dispatch(stmt_syntag_, tts); 73 | } 74 | extract_tree_tag_(); 75 | #if DEBUG_PRINT 76 | if (stmt_tree_.empty()) { 77 | Syntax_Tags::print(std::cerr << "Parsing ", stmt_syntag_) << " failed on\n"; 78 | print_me(std::cerr, false) << '\n'; 79 | } 80 | #endif 81 | return !stmt_tree_.empty(); 82 | } 83 | 84 | std::ostream &LL_Stmt::print_me(std::ostream &os, 85 | bool const print_prefix) const { 86 | if (empty()) 87 | os << "? : "; 88 | else { 89 | std::string line_label; 90 | if (ll().file_info) { 91 | line_label = ll().file_info->filename; 92 | } else { 93 | line_label = "(unknown file)"; 94 | } 95 | line_label.push_back(':'); 96 | 97 | if (print_prefix) { 98 | for (auto const &it : prefix_lines) { 99 | os << line_label << it->start_line() << ":\n" << *it; 100 | } 101 | } 102 | 103 | os << line_label << linenum() << ": "; 104 | render(os, cbegin(), cend()); 105 | } 106 | return os; 107 | } 108 | } // namespace FLPR 109 | -------------------------------------------------------------------------------- /src/flpr/Prgm_Tree.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Prgm_Tree.hh 13 | */ 14 | #ifndef FLPR_PRGM_TREE_HH 15 | #define FLPR_PRGM_TREE_HH 1 16 | 17 | #include "flpr/LL_Stmt.hh" 18 | #include "flpr/Stmt_Tree.hh" 19 | #include "flpr/Syntax_Tags.hh" 20 | #include "flpr/Tree.hh" 21 | 22 | #include 23 | #include 24 | 25 | namespace FLPR { 26 | namespace Prgm { 27 | 28 | //! The contents of each \c Prgm_Tree node 29 | class Prgm_Node_Data { 30 | public: 31 | using Stmt_Tree = FLPR::LL_Stmt::Stmt_Tree; 32 | using Stmt_Range = FLPR::SL_Range; 33 | 34 | //! default constructor 35 | constexpr Prgm_Node_Data() noexcept 36 | : syntag_{Syntax_Tags::UNKNOWN}, stmt_range_{}, stmt_data_(std::nullopt) { 37 | } 38 | //! interior node constructor 39 | constexpr explicit Prgm_Node_Data(int const syntag) noexcept 40 | : syntag_{syntag}, stmt_range_{}, stmt_data_(std::nullopt) {} 41 | //! leaf node constructor associated with a statement 42 | Prgm_Node_Data(int const syntag, 43 | FLPR::LL_STMT_SEQ::iterator const &ll_stmt_it) 44 | : syntag_{syntag}, stmt_range_{ll_stmt_it}, 45 | stmt_data_(std::in_place, ll_stmt_it) {} 46 | 47 | constexpr int syntag() const { return syntag_; } 48 | constexpr void syntag(int const newval) { syntag_ = newval; } 49 | Stmt_Range &stmt_range() noexcept { return stmt_range_; } 50 | 51 | constexpr bool is_stmt() const noexcept { return stmt_data_.has_value(); } 52 | Stmt_Tree &stmt_tree() { return stmt_data_->stmt_tree(); } 53 | Stmt_Tree const &stmt_tree() const { return stmt_data_->stmt_tree(); } 54 | LL_Stmt &ll_stmt() { return stmt_data_->ll_stmt(); } 55 | LL_Stmt const &ll_stmt() const { return stmt_data_->ll_stmt(); } 56 | FLPR::LL_STMT_SEQ::iterator ll_stmt_iter() const noexcept { 57 | return stmt_data_->ll_stmt_iter(); 58 | } 59 | 60 | private: 61 | //! The Syntax_Tags::Tags associated with this (sub)tree 62 | int syntag_; 63 | //! Track the range of LL_Stmt's covered by this node 64 | Stmt_Range stmt_range_; 65 | 66 | class Stmt_Data { 67 | public: 68 | Stmt_Data() = delete; 69 | Stmt_Data(Stmt_Data const &) = delete; 70 | constexpr Stmt_Data(FLPR::LL_STMT_SEQ::iterator const &ll_stmt_iter) 71 | : ll_stmt_iter_{ll_stmt_iter} {} 72 | FLPR::LL_Stmt const &ll_stmt() const noexcept { return *ll_stmt_iter_; } 73 | FLPR::LL_Stmt &ll_stmt() noexcept { return *ll_stmt_iter_; } 74 | FLPR::LL_STMT_SEQ::iterator ll_stmt_iter() const noexcept { 75 | return ll_stmt_iter_; 76 | } 77 | Stmt_Tree const &stmt_tree() const noexcept { 78 | return ll_stmt_iter_->stmt_tree(); 79 | } 80 | Stmt_Tree &stmt_tree() noexcept { return ll_stmt_iter_->stmt_tree(); } 81 | 82 | private: 83 | FLPR::LL_STMT_SEQ::iterator ll_stmt_iter_; 84 | }; 85 | 86 | //! Data for leaf-nodes associated with statements 87 | std::optional stmt_data_; 88 | }; 89 | 90 | std::ostream &operator<<(std::ostream &os, Prgm_Node_Data const &pn); 91 | } // namespace Prgm 92 | } // namespace FLPR 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/flpr/LL_Stmt_Src.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file LL_Stmt_Src.hh 13 | */ 14 | 15 | #ifndef FLPR_LL_STMT_SRC_HH 16 | #define FLPR_LL_STMT_SRC_HH 1 17 | 18 | #include "flpr/LL_Stmt.hh" 19 | #include "flpr/Logical_Line.hh" 20 | #include 21 | 22 | namespace FLPR { 23 | //! Presents an LL_SEQ as a sequence of statements 24 | /*! This is a fairly strange hybrid between an istream and an 25 | InputIterator. The similarity to istream comes from the fact that 26 | we don't know how many elements are going to be presented, so we 27 | advance and check the "goodness" of the source, rather than having 28 | a begin/end pair. It is similar to an InputIterator in that you 29 | can dereference it without copying, because it constructs a buffer 30 | of LL_Stmt. 31 | */ 32 | class LL_Stmt_Src { 33 | public: 34 | using value_type = LL_Stmt; 35 | using reference = LL_Stmt &; 36 | using const_reference = LL_Stmt const &; 37 | using pointer = LL_Stmt *; 38 | using const_pointer = LL_Stmt const *; 39 | 40 | public: 41 | LL_Stmt_Src() = delete; 42 | LL_Stmt_Src(LL_Stmt_Src const &) = default; 43 | LL_Stmt_Src(LL_Stmt_Src &&) = default; 44 | LL_Stmt_Src &operator=(LL_Stmt_Src const &) = default; 45 | LL_Stmt_Src &operator=(LL_Stmt_Src &&) = default; 46 | 47 | //! Construct a source for the given Logical_Line sequence 48 | explicit LL_Stmt_Src(LL_SEQ &ll, bool const do_advance = true) 49 | : it_{ll}, curr_{buf_.end()} { 50 | if (do_advance) 51 | advance(); 52 | } 53 | 54 | //! Construct a source for a single Logical_Line 55 | explicit LL_Stmt_Src(LL_SEQ::iterator ll, bool const do_advance = true) 56 | : it_{ll}, curr_{buf_.end()} { 57 | if (do_advance) 58 | advance(); 59 | } 60 | 61 | //! Test to see if the source is still good 62 | explicit operator bool() { 63 | if (curr_ != buf_.end()) { 64 | return true; 65 | } 66 | return more_avail_(); 67 | } 68 | //! Advance to the next LL_Stmt. 69 | /*! \returns false if there are none, true otherwise */ 70 | bool advance() { 71 | bool retval{true}; 72 | if (curr_ != buf_.end()) { 73 | std::advance(curr_, 1); 74 | } 75 | if (curr_ == buf_.end()) { 76 | buf_.clear(); 77 | retval = more_avail_(); 78 | } 79 | return retval; 80 | } 81 | 82 | LL_Stmt &&move() { 83 | assert(!buf_.empty()); 84 | assert(curr_ != buf_.end()); 85 | return std::move(*curr_); 86 | } 87 | 88 | private: 89 | //! Return true if there are more statements available 90 | bool more_avail_() { 91 | while (buf_.empty() && it_) 92 | refill_(); 93 | return !buf_.empty(); 94 | } 95 | //! Attempt to refill the buffer from the next non-trivial LL 96 | void refill_(); 97 | //! Where we are in the input sequence 98 | SL_Range_Iterator it_; 99 | using BUF_T = std::list; 100 | //! Storage for the local LL_Stmt 101 | BUF_T buf_; 102 | //! Where we are in the buffer 103 | typename BUF_T::iterator curr_; 104 | }; 105 | } // namespace FLPR 106 | #endif 107 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | 78 | -------------------------------------------------------------------------------- /tests/test_line_accum.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #include "flpr/Line_Accum.hh" 12 | #include "test_helpers.hh" 13 | #include 14 | 15 | using FLPR::Line_Accum; 16 | 17 | // One line, no offset 18 | bool simple() { 19 | Line_Accum la; 20 | int ln, cn; 21 | la.add_line(0, 0, 1, "foo", 0); 22 | TEST_STR("foo", la.accum()); 23 | la.linecolno(0, ln, cn); 24 | TEST_EQ(ln, 0); 25 | TEST_EQ(cn, 1); 26 | la.linecolno(1, ln, cn); 27 | TEST_EQ(ln, 0); 28 | TEST_EQ(cn, 2); 29 | la.linecolno(2, ln, cn); 30 | TEST_EQ(ln, 0); 31 | TEST_EQ(cn, 3); 32 | return true; 33 | } 34 | 35 | // One line, 2 char offset 36 | bool simple1() { 37 | Line_Accum la; 38 | int ln, cn; 39 | la.add_line(0, 0, 2, "foo", 0); 40 | TEST_STR("foo", la.accum()); 41 | la.linecolno(0, ln, cn); 42 | TEST_EQ(ln, 0); 43 | TEST_EQ(cn, 2); 44 | la.linecolno(1, ln, cn); 45 | TEST_EQ(ln, 0); 46 | TEST_EQ(cn, 3); 47 | la.linecolno(2, ln, cn); 48 | TEST_EQ(ln, 0); 49 | TEST_EQ(cn, 4); 50 | return true; 51 | } 52 | 53 | // One line (#3), 2 char offset 54 | bool simple2() { 55 | Line_Accum la; 56 | int ln, cn; 57 | la.add_line(3, 0, 2, "foo", 0); 58 | TEST_STR("foo", la.accum()); 59 | la.linecolno(0, ln, cn); 60 | TEST_EQ(ln, 3); 61 | TEST_EQ(cn, 2); 62 | la.linecolno(1, ln, cn); 63 | TEST_EQ(ln, 3); 64 | TEST_EQ(cn, 3); 65 | la.linecolno(2, ln, cn); 66 | TEST_EQ(ln, 3); 67 | TEST_EQ(cn, 4); 68 | return true; 69 | } 70 | 71 | bool subname() { 72 | Line_Accum la; 73 | int ln, cn; 74 | la.add_line(11, 0, 1, "subroutine b(kdd)", 0); 75 | la.linecolno(11, ln, cn); 76 | TEST_EQ(ln, 11); 77 | TEST_EQ(cn, 12); 78 | return true; 79 | } 80 | 81 | // Two line, 2 char offset 82 | bool twoline1() { 83 | Line_Accum la; 84 | int ln, cn; 85 | la.add_line(3, 0, 2, "foo", 1); 86 | la.add_line(4, 0, 5, "bar", 0); 87 | TEST_STR("foo bar", la.accum()); 88 | la.linecolno(0, ln, cn); 89 | TEST_EQ(ln, 3); 90 | TEST_EQ(cn, 2); 91 | la.linecolno(1, ln, cn); 92 | TEST_EQ(ln, 3); 93 | TEST_EQ(cn, 3); 94 | la.linecolno(2, ln, cn); 95 | TEST_EQ(ln, 3); 96 | TEST_EQ(cn, 4); 97 | la.linecolno(3, ln, cn); 98 | TEST_EQ(ln, 3); 99 | TEST_EQ(cn, 5); 100 | la.linecolno(4, ln, cn); 101 | TEST_EQ(ln, 4); 102 | TEST_EQ(cn, 5); 103 | la.linecolno(5, ln, cn); 104 | TEST_EQ(ln, 4); 105 | TEST_EQ(cn, 6); 106 | la.linecolno(6, ln, cn); 107 | TEST_EQ(ln, 4); 108 | TEST_EQ(cn, 7); 109 | return true; 110 | } 111 | 112 | bool continued_string() { 113 | Line_Accum la; 114 | la.add_line(1, 0, 6, 115 | "print *, " 116 | " 'abc", 117 | 0); 118 | la.add_line(2, 0, 6, "def'", 0); 119 | TEST_STR("print *, " 120 | " 'abcdef'", 121 | la.accum()); 122 | return true; 123 | } 124 | 125 | int main() { 126 | TEST_MAIN_DECL; 127 | TEST(simple); 128 | TEST(simple1); 129 | TEST(simple2); 130 | TEST(subname); 131 | TEST(twoline1); 132 | TEST(continued_string); 133 | TEST_MAIN_REPORT; 134 | } 135 | -------------------------------------------------------------------------------- /tests/test_syntag_sanity.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /* 12 | Testing for Syntax_Tag entry consistency. This is more a sanity check 13 | on the contents of Syntax_Tag_Defs.hh than anything. 14 | */ 15 | #include "flpr/Syntax_Tags.hh" 16 | #include "test_helpers.hh" 17 | #include 18 | 19 | /* The non-control syntags are stored in alphabetical order, and each syntag 20 | type class (KW_, PG_, SG_, TK_) has an entity to represent the (open) lower 21 | bound of the class indices (e.g. SG_000_LB) and an (open) upper bound 22 | (SG_ZZZ_UB). Check that the UB and LBs are consecutive. */ 23 | bool tag_bounds() { 24 | TEST_TRUE(FLPR::Syntax_Tags::KW_000_LB < FLPR::Syntax_Tags::KW_ZZZ_UB); 25 | TEST_EQ(FLPR::Syntax_Tags::PG_000_LB, FLPR::Syntax_Tags::KW_ZZZ_UB + 1); 26 | TEST_TRUE(FLPR::Syntax_Tags::PG_000_LB < FLPR::Syntax_Tags::PG_ZZZ_UB); 27 | TEST_EQ(FLPR::Syntax_Tags::SG_000_LB, FLPR::Syntax_Tags::PG_ZZZ_UB + 1); 28 | TEST_TRUE(FLPR::Syntax_Tags::SG_000_LB < FLPR::Syntax_Tags::SG_ZZZ_UB); 29 | TEST_EQ(FLPR::Syntax_Tags::TK_000_LB, FLPR::Syntax_Tags::SG_ZZZ_UB + 1); 30 | TEST_TRUE(FLPR::Syntax_Tags::TK_000_LB < FLPR::Syntax_Tags::TK_ZZZ_UB); 31 | TEST_EQ(FLPR::Syntax_Tags::CLIENT_EXTENSION, 32 | FLPR::Syntax_Tags::TK_ZZZ_UB + 1); 33 | return true; 34 | } 35 | 36 | bool kw_type() { 37 | for (int i = FLPR::Syntax_Tags::KW_000_LB + 1; 38 | i < FLPR::Syntax_Tags::KW_ZZZ_UB; ++i) { 39 | TEST_INT_LABEL(FLPR::Syntax_Tags::label(i), FLPR::Syntax_Tags::type(i), 4); 40 | } 41 | return true; 42 | } 43 | 44 | bool sg_stmt_type() { 45 | for (int i = FLPR::Syntax_Tags::SG_000_LB + 1; 46 | i < FLPR::Syntax_Tags::SG_ZZZ_UB; ++i) { 47 | std::string label = FLPR::Syntax_Tags::label(i); 48 | auto pos = label.find_last_of('-'); 49 | std::string last_term = label.substr(pos + 1); 50 | if (last_term == "stmt") { 51 | TEST_INT_LABEL(label, FLPR::Syntax_Tags::type(i), 5); 52 | } else { 53 | TEST_OR_INT_LABEL(label, FLPR::Syntax_Tags::type(i), 1, 2); 54 | } 55 | } 56 | return true; 57 | } 58 | 59 | bool tk_type() { 60 | for (int i = FLPR::Syntax_Tags::TK_000_LB + 1; 61 | i < FLPR::Syntax_Tags::TK_ZZZ_UB; ++i) { 62 | TEST_INT_LABEL(FLPR::Syntax_Tags::label(i), FLPR::Syntax_Tags::type(i), 3); 63 | } 64 | return true; 65 | } 66 | 67 | bool ext_test() { 68 | const int ext_tag1 = FLPR::Syntax_Tags::CLIENT_EXTENSION; 69 | const int ext_tag2 = ext_tag1 + 1; 70 | const int ext_tag3 = ext_tag1 + 2; 71 | // the registration order shouldn't matter 72 | FLPR::Syntax_Tags::register_ext(ext_tag3, "mytag3", 5); 73 | FLPR::Syntax_Tags::register_ext(ext_tag1, "mytag1", 1); 74 | TEST_INT(FLPR::Syntax_Tags::type(ext_tag1), 1); 75 | TEST_INT(FLPR::Syntax_Tags::type(ext_tag2), 4); // default for unassigned 76 | TEST_INT(FLPR::Syntax_Tags::type(ext_tag3), 5); 77 | TEST_STR("mytag1", FLPR::Syntax_Tags::label(ext_tag1)); 78 | TEST_STR("", FLPR::Syntax_Tags::label(ext_tag2)); 79 | TEST_STR("mytag3", FLPR::Syntax_Tags::label(ext_tag3)); 80 | return true; 81 | } 82 | 83 | int main() { 84 | TEST_MAIN_DECL; 85 | TEST(tag_bounds); 86 | TEST(sg_stmt_type); 87 | TEST(kw_type); 88 | TEST(tk_type); 89 | TEST(ext_test); 90 | TEST_MAIN_REPORT; 91 | } 92 | -------------------------------------------------------------------------------- /src/flpr/Line_Accum.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Line_Accum.cc 13 | */ 14 | 15 | #include "flpr/Line_Accum.hh" 16 | #include 17 | #include 18 | #include 19 | 20 | namespace FLPR { 21 | void Line_Accum::add_line(int const file_lineno, int const num_left_spaces, 22 | int const main_txt_file_colno, 23 | std::string const &main_txt, 24 | int const num_right_spaces) { 25 | /* main_txt starts at lli_to_accum_offset_[i], and relates to file line 26 | numbers lli_to_file_line_num_[i], and column number 27 | lli_to_file_column_num_[i]. */ 28 | if (!lli_to_accum_offset_.empty() && num_left_spaces > 0) { 29 | accum_ += ' '; 30 | } 31 | lli_to_accum_offset_.push_back(accum_.size()); 32 | lli_to_file_line_num_.push_back(file_lineno); 33 | lli_to_file_column_num_.push_back(main_txt_file_colno); 34 | accum_ += main_txt; 35 | if (num_right_spaces > 0) { 36 | accum_ += ' '; 37 | } 38 | } 39 | 40 | bool Line_Accum::linecolno(int accum_offset, int &lineno, int &colno) const { 41 | if (lli_to_accum_offset_.empty()) 42 | return false; 43 | 44 | /* find the LLI */ 45 | size_t const N = lli_to_accum_offset_.size(); 46 | size_t lli{0}; 47 | while (lli + 1 < N && lli_to_accum_offset_[lli + 1] <= accum_offset) { 48 | lli += 1; 49 | } 50 | 51 | assert(lli < lli_to_accum_offset_.size()); 52 | assert(lli_to_accum_offset_[lli] <= accum_offset); 53 | 54 | /* adjust to be the offset within the main_txt for this LLI */ 55 | accum_offset -= lli_to_accum_offset_[lli]; 56 | lineno = lli_to_file_line_num_[lli]; 57 | colno = lli_to_file_column_num_[lli] + accum_offset; 58 | 59 | assert(colno > 0); 60 | 61 | return true; 62 | } 63 | 64 | bool Line_Accum::linecolno(int accum_offset, int &lineno, int &colno, 65 | int &txt_lineno, int &txt_colno) const { 66 | if (lli_to_accum_offset_.empty()) 67 | return false; 68 | 69 | /* find the LLI */ 70 | size_t const N = lli_to_accum_offset_.size(); 71 | size_t lli{0}; 72 | while (lli + 1 < N && lli_to_accum_offset_[lli + 1] <= accum_offset) { 73 | lli += 1; 74 | } 75 | 76 | assert(lli < lli_to_accum_offset_.size()); 77 | assert(lli_to_accum_offset_[lli] <= accum_offset); 78 | 79 | /* adjust to be the offset within the main_txt for this LLI */ 80 | accum_offset -= lli_to_accum_offset_[lli]; 81 | lineno = lli_to_file_line_num_[lli]; 82 | colno = lli_to_file_column_num_[lli] + accum_offset; 83 | txt_lineno = (int)lli; 84 | txt_colno = accum_offset; 85 | 86 | assert(colno > 0); 87 | assert(txt_lineno >= 0); 88 | assert(txt_lineno < static_cast(lli_to_file_line_num_.size())); 89 | assert(txt_colno >= 0); 90 | 91 | return true; 92 | } 93 | 94 | std::ostream &Line_Accum::print(std::ostream &os) const { 95 | os << '\"' << accum_ << '\"' << '\n'; 96 | for (auto const &i : lli_to_accum_offset_) 97 | os << i << ' '; 98 | os << '\n'; 99 | for (auto const &i : lli_to_file_line_num_) 100 | os << i << ' '; 101 | os << '\n'; 102 | for (auto const &i : lli_to_file_column_num_) 103 | os << i << ' '; 104 | os << '\n'; 105 | return os; 106 | } 107 | } // namespace FLPR 108 | -------------------------------------------------------------------------------- /apps/parse_files.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file parse_files.cc 13 | 14 | This executable just runs the FLPR parser on a list of files. 15 | */ 16 | 17 | #include "flpr/Logical_File.hh" 18 | #include "flpr/Prgm_Parsers.hh" 19 | #include "flpr/Prgm_Tree.hh" 20 | #include 21 | #include 22 | #include 23 | 24 | using Parse = FLPR::Prgm::Parsers; 25 | using Parse_Tree = Parse::Prgm_Tree; 26 | 27 | struct File { 28 | FLPR::Logical_File logical_file; 29 | Parse_Tree parse_tree; 30 | }; 31 | 32 | bool read_file(std::string const &filename, std::vector &files); 33 | bool parse_cmd_line(std::vector &filenames, int argc, 34 | char *const argv[]); 35 | 36 | int main(int argc, char *const argv[]) { 37 | std::vector filenames; 38 | if (!parse_cmd_line(filenames, argc, argv)) { 39 | std::cerr << "exiting on error." << std::endl; 40 | return 1; 41 | } 42 | 43 | std::vector files; 44 | for (auto const &f : filenames) { 45 | read_file(f, files); 46 | } 47 | std::cout << "done." << std::endl; 48 | return 0; 49 | } 50 | 51 | bool read_file(std::string const &filename, std::vector &files) { 52 | File f; 53 | std::cout << "Processing: '" << filename << "'" 54 | << "\n\tscanning..." << std::endl; 55 | if (!f.logical_file.read_and_scan(filename, 0)) { 56 | std::cout << "\tread/scan FAILED" << std::endl; 57 | return false; 58 | } 59 | std::cout << "\tscan created " << f.logical_file.lines.size() 60 | << " logical lines from " << f.logical_file.num_input_lines 61 | << " input text lines." << std::endl; 62 | std::cout << "\tparsing..." << std::endl; 63 | f.logical_file.make_stmts(); 64 | Parse::State state(f.logical_file.ll_stmts); 65 | auto result{Parse::program(state)}; 66 | if (!result.match) { 67 | std::cout << "\tparsing FAILED" << std::endl; 68 | return false; 69 | } 70 | 71 | auto c{result.parse_tree.ccursor()}; 72 | std::cout << "\troot rule \"" << *c << "\" has " << c.node().num_branches() 73 | << " branches. " << '\n'; 74 | 75 | f.parse_tree.swap(result.parse_tree); 76 | 77 | return true; 78 | } 79 | 80 | bool file_list_from_file(std::vector &filenames, 81 | char const *file_list_name) { 82 | std::ifstream is(file_list_name); 83 | if (!is) { 84 | std::cerr << "Unable to open file-list \"" << file_list_name << "\"" 85 | << std::endl; 86 | return false; 87 | } 88 | const size_t orig_size = filenames.size(); 89 | for (std::string fname; std::getline(is, fname);) { 90 | filenames.emplace_back(std::string{}); 91 | filenames.back().swap(fname); 92 | } 93 | is.close(); 94 | return filenames.size() > orig_size; 95 | } 96 | 97 | bool parse_cmd_line(std::vector &filenames, int argc, 98 | char *const argv[]) { 99 | int ch; 100 | while ((ch = getopt(argc, argv, "f:")) != -1) { 101 | switch (ch) { 102 | case 'f': 103 | if (!file_list_from_file(filenames, optarg)) 104 | return false; 105 | break; 106 | default: 107 | std::cerr << "unknown option" << std::endl; 108 | return false; 109 | } 110 | } 111 | argc -= optind; 112 | argv += optind; 113 | for (int i = 0; i < argc; ++i) { 114 | filenames.emplace_back(std::string{argv[i]}); 115 | } 116 | return true; 117 | }; 118 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: MultiLine 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakInheritanceList: BeforeColon 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializersBeforeComma: false 45 | BreakConstructorInitializers: BeforeColon 46 | BreakAfterJavaFieldAnnotations: false 47 | BreakStringLiterals: true 48 | ColumnLimit: 80 49 | CommentPragmas: '^ IWYU pragma:' 50 | CompactNamespaces: false 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 52 | ConstructorInitializerIndentWidth: 4 53 | ContinuationIndentWidth: 4 54 | Cpp11BracedListStyle: true 55 | DerivePointerAlignment: false 56 | DisableFormat: false 57 | ExperimentalAutoDetectBinPacking: false 58 | FixNamespaceComments: true 59 | ForEachMacros: 60 | - foreach 61 | - Q_FOREACH 62 | - BOOST_FOREACH 63 | IncludeBlocks: Preserve 64 | IncludeCategories: 65 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 66 | Priority: 2 67 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 68 | Priority: 3 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: false 73 | IndentPPDirectives: None 74 | IndentWidth: 2 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: true 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: None 83 | ObjCBinPackProtocolList: Auto 84 | ObjCBlockIndentWidth: 2 85 | ObjCSpaceAfterProperty: false 86 | ObjCSpaceBeforeProtocolList: true 87 | PenaltyBreakAssignment: 2 88 | PenaltyBreakBeforeFirstCallParameter: 19 89 | PenaltyBreakComment: 300 90 | PenaltyBreakFirstLessLess: 120 91 | PenaltyBreakString: 1000 92 | PenaltyBreakTemplateDeclaration: 10 93 | PenaltyExcessCharacter: 1000000 94 | PenaltyReturnTypeOnItsOwnLine: 60 95 | PointerAlignment: Right 96 | ReflowComments: true 97 | SortIncludes: true 98 | SortUsingDeclarations: true 99 | SpaceAfterCStyleCast: false 100 | SpaceAfterTemplateKeyword: true 101 | SpaceBeforeAssignmentOperators: true 102 | SpaceBeforeCpp11BracedList: false 103 | SpaceBeforeCtorInitializerColon: true 104 | SpaceBeforeInheritanceColon: true 105 | SpaceBeforeParens: ControlStatements 106 | SpaceBeforeRangeBasedForLoopColon: true 107 | SpaceInEmptyParentheses: false 108 | SpacesBeforeTrailingComments: 1 109 | SpacesInAngles: false 110 | SpacesInContainerLiterals: true 111 | SpacesInCStyleCastParentheses: false 112 | SpacesInParentheses: false 113 | SpacesInSquareBrackets: false 114 | Standard: Cpp11 115 | StatementMacros: 116 | - Q_UNUSED 117 | - QT_REQUIRE_VERSION 118 | TabWidth: 8 119 | UseTab: Never 120 | ... 121 | 122 | -------------------------------------------------------------------------------- /apps/flpr_show_cst.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /* 12 | \file flpr_show_cst.cc 13 | 14 | Diagnostic utility to parse lines from the standard input and, if successful, 15 | print out the concrete syntax tree. 16 | */ 17 | 18 | #include "flpr/flpr.hh" 19 | #include 20 | 21 | int main() { 22 | /* Setup options */ 23 | FLPR::File_Type file_type = FLPR::File_Type::FREEFMT; 24 | 25 | /* Read lines from standard input */ 26 | using Raw_Lines = FLPR::Logical_File::Line_Buf; 27 | Raw_Lines raw_lines; 28 | std::string line; 29 | /* Print banner etc to cerr to allow for easy redirect of output */ 30 | std::cerr << '\n' 31 | << "==============================\n" 32 | << "FLPR Show Concrete Syntax Tree\n" 33 | << "==============================\n\n" 34 | << "Enter free-form Fortran statements, blank line or " 35 | "Ctrl-D/EOF to end input: " 36 | << std::endl; 37 | while (std::getline(std::cin, line)) { 38 | if (!line.empty()) { 39 | raw_lines.emplace_back(line); 40 | } else 41 | break; 42 | } 43 | if (raw_lines.empty()) { 44 | std::cerr << "No lines entered." << std::endl; 45 | return 1; 46 | } 47 | 48 | /* Scan text and produce a Logical_File (a sequence of Logical_Lines) */ 49 | FLPR::Logical_File logical_file; 50 | /* Set a fake filename just to make the output look pretty. This isn't 51 | required. */ 52 | logical_file.file_info = 53 | std::make_shared(std::string("Line"), file_type); 54 | 55 | bool scan_okay{false}; 56 | 57 | switch (file_type) { 58 | case FLPR::File_Type::FIXEDFMT: 59 | scan_okay = logical_file.scan_fixed(raw_lines, 72); 60 | break; 61 | case FLPR::File_Type::FREEFMT: 62 | scan_okay = logical_file.scan_free(raw_lines); 63 | break; 64 | default: 65 | std::cerr << "Unhandled file form type." << std::endl; 66 | } 67 | if (!scan_okay) { 68 | std::cerr << "scan failed, exiting." << std::endl; 69 | return 2; 70 | } 71 | 72 | /* Create statements from logical lines */ 73 | logical_file.make_stmts(); 74 | 75 | /* Iterate across the LL_Stmts, seeing which parsers match. Note that 76 | multiple parsers can match. For example, text accepted by assignment-stmt 77 | will also match against action-stmt and forall-assignment-stmt. */ 78 | for (auto &stmt : logical_file.ll_stmts) { 79 | int results{0}; 80 | std::cerr << "--------------------------------------------------------\n" 81 | << "Parsing statement: \"" << stmt << "\"\n" 82 | << "--------------------------------------------------------\n"; 83 | /* Build a token stream for this statement */ 84 | FLPR::TT_Stream ts(stmt); 85 | /* Iterate across all the statement grammar (SG) parsers... */ 86 | for (int sg_id = FLPR::Syntax_Tags::SG_000_LB + 1; 87 | sg_id < FLPR::Syntax_Tags::SG_ZZZ_UB; ++sg_id) { 88 | /* ...looking for top-level stmt parsers... */ 89 | if (FLPR::Syntax_Tags::type(sg_id) == 5) { 90 | FLPR::Stmt::Stmt_Tree st = FLPR::Stmt::parse_stmt_dispatch(sg_id, ts); 91 | /* ... that accept this input. */ 92 | if (st) { 93 | results += 1; 94 | std::cout << results << ": " << st << '\n'; 95 | /* do this so that subsequent parsers can attempt to match. */ 96 | ts.rewind(); 97 | } 98 | } 99 | } 100 | if (!results) { 101 | std::cout << "Unrecognized: " << stmt << '\n'; 102 | } 103 | } 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /src/flpr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 2 | # 3 | # This is open source software; you can redistribute it and/or modify it 4 | # under the terms of the BSD-3 License. If software is modified to produce 5 | # derivative works, such modified software should be clearly marked, so as 6 | # not to confuse it with the version available from LANL. Full text of the 7 | # BSD-3 License can be found in the LICENSE file of the repository. 8 | 9 | 10 | # FLPR/src/CMakeLists.txt 11 | 12 | # ---------------------------- COMMON LIBRARY ----------------------------- 13 | 14 | find_package(FLEX 2.6) 15 | 16 | FLEX_TARGET(Fortran_Scanner scan_fort.l 17 | ${FLPR_BINARY_DIR}/scan_fort.cc 18 | DEFINES_FILE ${FLPR_BINARY_DIR}/scan_fort.hh) 19 | 20 | set(Libflpr_SRCS 21 | File_Info.cc 22 | File_Line.cc 23 | Indent_Table.cc 24 | LL_Stmt.cc 25 | LL_Stmt_Src.cc 26 | LL_TT_Range.cc 27 | Line_Accum.cc 28 | Logical_File.cc 29 | Logical_Line.cc 30 | Prgm_Tree.cc 31 | Stmt_Parser_Exts.cc 32 | Stmt_Tree.cc 33 | Syntax_Tags.cc 34 | Token_Text.cc 35 | TT_Stream.cc 36 | parse_stmt.cc 37 | scan_fort.l 38 | utils.cc 39 | ) 40 | 41 | set(flpr_headers 42 | File_Info.hh 43 | File_Line.hh 44 | Indent_Table.hh 45 | Label_Stack.hh 46 | LL_Stmt.hh 47 | LL_Stmt_Src.hh 48 | LL_TT_Range.hh 49 | Line_Accum.hh 50 | Logical_File.hh 51 | Logical_Line.hh 52 | Parsed_File.hh 53 | Parser_Result.hh 54 | Prgm_Parsers.hh 55 | Prgm_Parsers_impl.hh 56 | Prgm_Parsers_utils.hh 57 | Prgm_Tree.hh 58 | Procedure.hh 59 | Procedure_Visitor.hh 60 | Range_Partition.hh 61 | Safe_List.hh 62 | Stmt_Parser_Exts.hh 63 | Stmt_Parsers.hh 64 | Stmt_Tree.hh 65 | Syntax_Tags.hh 66 | Syntax_Tags_Defs.hh 67 | TT_Stream.hh 68 | Token_Text.hh 69 | Tree.hh 70 | flpr.hh 71 | parse_stmt.hh 72 | utils.hh 73 | ) 74 | 75 | 76 | set_source_files_properties(${FLEX_Fortran_Scanner_OUTPUTS} 77 | PROPERTIES GENERATED TRUE) 78 | set_source_files_properties(Logical_Line.cc 79 | PROPERTIES OBJECT_DEPENDS ${FLEX_Fortran_Scanner_OUTPUT_HEADER}) 80 | 81 | # Make sure to have the FLEX outputs listed first, so the built header 82 | # is available for other compilation. 83 | add_library(flpr 84 | ${FLEX_Fortran_Scanner_OUTPUTS} 85 | ${Libflpr_SRCS} 86 | ) 87 | target_compile_features(flpr PUBLIC cxx_std_17) 88 | set_target_properties(flpr PROPERTIES CXX_EXTENSIONS OFF) 89 | 90 | # We need the CURRENT_BINARY include so that non-generated source can 91 | # include a FLEX-generated header 92 | target_include_directories(flpr 93 | PUBLIC 94 | $ 95 | $ 96 | PRIVATE 97 | $ 98 | $ 99 | ) 100 | 101 | # Installation Info 102 | include(GNUInstallDirs) 103 | 104 | install(TARGETS flpr 105 | EXPORT FLPRTargets 106 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 107 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 108 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 109 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 110 | ) 111 | 112 | # Install the headers 113 | install( 114 | FILES ${flpr_headers} 115 | DESTINATION include/flpr) 116 | 117 | 118 | 119 | # Write out config-file package information 120 | 121 | include(CMakePackageConfigHelpers) 122 | 123 | write_basic_package_version_file( 124 | ${FLPR_BINARY_DIR}/FLPRConfigVersion.cmake 125 | VERSION ${PROJECT_VERSION} 126 | COMPATIBILITY SameMajorVersion ) 127 | 128 | export(EXPORT FLPRTargets 129 | FILE ${FLPR_BINARY_DIR}/FLPRTargets.cmake 130 | ) 131 | 132 | configure_package_config_file(FLPRConfig.cmake.in 133 | ${FLPR_BINARY_DIR}/FLPRConfig.cmake 134 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake 135 | PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR) 136 | 137 | install(EXPORT FLPRTargets 138 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake 139 | ) 140 | 141 | install(FILES ${FLPR_BINARY_DIR}/FLPRConfig.cmake 142 | ${FLPR_BINARY_DIR}/FLPRConfigVersion.cmake 143 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake ) 144 | 145 | 146 | -------------------------------------------------------------------------------- /apps/flpr_format_base.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #ifndef FLPR_FORMAT_BASE_HH 12 | #define FLPR_FORMAT_BASE_HH 1 13 | 14 | #include "Timer.hh" 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /* Manage the transformation options */ 21 | struct Options { 22 | enum Filter_Tags : size_t { 23 | ELABORATE_END_STMTS, 24 | FIXED_TO_FREE, 25 | COL72, 26 | REINDENT, 27 | REMOVE_EMPTY_STMTS, 28 | SPLIT_COMPOUND_STMTS, 29 | NUM_FILTERS /* must be last */ 30 | }; 31 | 32 | Options() noexcept 33 | : write_inplace_{false}, verbose_{false}, do_timing_{false}, quiet_{ 34 | false} { 35 | disable_all_filters(); 36 | } 37 | void disable_all_filters() noexcept { filters_.fill(false); } 38 | void enable_all_filters() noexcept { filters_.fill(true); } 39 | constexpr bool &operator[](Filter_Tags const tag) noexcept { 40 | return filters_[tag]; 41 | } 42 | constexpr bool operator[](Filter_Tags const tag) const noexcept { 43 | return filters_[tag]; 44 | } 45 | constexpr void set_write_inplace(bool const val) noexcept { 46 | write_inplace_ = val; 47 | } 48 | constexpr bool write_inplace() const noexcept { return write_inplace_; } 49 | constexpr void set_verbose(bool const val) noexcept { verbose_ = val; } 50 | constexpr bool verbose() const noexcept { return verbose_; } 51 | constexpr void set_do_timing(bool const val) noexcept { do_timing_ = val; } 52 | constexpr bool do_timing() const noexcept { return do_timing_; } 53 | constexpr void set_quiet(bool const val) noexcept { quiet_ = val; } 54 | constexpr bool quiet() const noexcept { return quiet_; } 55 | constexpr void set_do_output(bool const val) noexcept { do_output_ = val; } 56 | constexpr bool do_output() const noexcept { return do_output_; } 57 | 58 | private: 59 | bool write_inplace_; 60 | bool verbose_; 61 | bool do_timing_; 62 | bool quiet_; 63 | bool do_output_; 64 | std::array filters_; 65 | }; 66 | 67 | /* This class contains and manages the Logical_Lines, LL_Stmts, Parse_Tree 68 | related to one input file */ 69 | using File = FLPR::Parsed_File<>; 70 | 71 | bool parse_cmd_line(std::vector &filenames, Options &options, 72 | int argc, char *const argv[]); 73 | int flpr_format_file(File &file, Options const &options, 74 | FLPR::Indent_Table const &indents); 75 | void write_file(std::ostream &os, File const &file); 76 | 77 | #define OPT(T) Options::T 78 | /* -------------------------------------------------------------------------- */ 79 | 80 | #define VERBOSE_BEGIN(OP) \ 81 | { \ 82 | Timer t; \ 83 | if (options.verbose()) { \ 84 | std::cerr << "Performing " << OP << "... " << std::flush; \ 85 | if (options.do_timing()) \ 86 | t.start(); \ 87 | } 88 | #define VERBOSE_END \ 89 | if (options.verbose()) { \ 90 | if (options.do_timing()) { \ 91 | t.stop(); \ 92 | std::cerr << "done (" << t << ")." << std::endl; \ 93 | } else { \ 94 | std::cerr << "done." << std::endl; \ 95 | } \ 96 | } \ 97 | } 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/flpr/Prgm_Parsers.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Prgm_Parsers.hh 13 | */ 14 | #ifndef FLPR_PRGM_PARSERS_HH 15 | #define FLPR_PRGM_PARSERS_HH 1 16 | 17 | #include "flpr/LL_Stmt.hh" 18 | #include "flpr/Label_Stack.hh" 19 | #include "flpr/Parser_Result.hh" 20 | #include "flpr/Prgm_Tree.hh" 21 | #include "flpr/Tree.hh" 22 | #include "flpr/parse_stmt.hh" 23 | #include 24 | 25 | #define FLPR_TRACE_PG 0 26 | 27 | namespace FLPR { 28 | 29 | //! All of the parser information for program-level constructs 30 | namespace Prgm { 31 | 32 | //! The parsers that organize statements into program structures 33 | /*! This is a wrapper for a bunch of static functions, used so that the client 34 | can extend the \c Node_Data type or provide an alternative \c Stmt_Src */ 35 | template struct Parsers { 36 | //! Define the Prgm_Tree 37 | /*! Node_Data should be (derived from) FLPR::Prgm::PT_Node_Data */ 38 | using Prgm_Tree = FLPR::Tree; 39 | //! The result from each parser in Prgm::Parsers 40 | using PP_Result = FLPR::details_::Parser_Result; 41 | 42 | class State { 43 | private: 44 | SL_Range stmt_range_; 45 | 46 | public: 47 | explicit State(LL_STMT_SEQ &ll_stmts) 48 | : stmt_range_(ll_stmts), ss{stmt_range_} {} 49 | explicit State(SL_Range const &ll_stmt_range) 50 | : stmt_range_(ll_stmt_range), ss{stmt_range_} {} 51 | SL_Range_Iterator ss; 52 | Label_Stack do_label_stack; 53 | }; 54 | 55 | static PP_Result associate_construct(State &state); 56 | static PP_Result block(State &state); 57 | static PP_Result block_construct(State &state); 58 | static PP_Result block_specification_part(State &state); 59 | static PP_Result case_construct(State &state); 60 | static PP_Result component_part(State &state); 61 | static PP_Result declaration_construct(State &state); 62 | static PP_Result derived_type_def(State &state); 63 | static PP_Result do_construct(State &state); 64 | static PP_Result enum_def(State &state); 65 | static PP_Result executable_construct(State &state); 66 | static PP_Result execution_part(State &state); 67 | static PP_Result execution_part_construct(State &state); 68 | static PP_Result external_subprogram(State &state); 69 | static PP_Result forall_body_construct(State &ss); 70 | static PP_Result forall_construct(State &ss); 71 | static PP_Result function_subprogram(State &state); 72 | static PP_Result if_construct(State &state); 73 | static PP_Result implicit_part(State &state); 74 | static PP_Result implicit_part_stmt(State &state); 75 | static PP_Result interface_block(State &state); 76 | static PP_Result interface_body(State &state); 77 | static PP_Result interface_specification(State &state); 78 | static PP_Result internal_subprogram(State &state); 79 | static PP_Result internal_subprogram_part(State &state); 80 | static PP_Result main_program(State &ss); 81 | static PP_Result module(State &ss); 82 | static PP_Result module_subprogram(State &ss); 83 | static PP_Result module_subprogram_part(State &ss); 84 | static PP_Result other_specification_stmt(State &state); 85 | static PP_Result program(State &state); 86 | static PP_Result program_unit(State &state); 87 | static PP_Result select_rank_construct(State &state); 88 | static PP_Result select_type_construct(State &state); 89 | static PP_Result separate_module_subprogram(State &ss); 90 | static PP_Result specification_construct(State &state); 91 | static PP_Result specification_part(State &state); 92 | static PP_Result subroutine_subprogram(State &state); 93 | static PP_Result type_bound_procedure_part(State &state); 94 | static PP_Result where_body_construct(State &state); 95 | static PP_Result where_construct(State &state); 96 | 97 | #include "Prgm_Parsers_utils.hh" 98 | }; 99 | 100 | #include "Prgm_Parsers_impl.hh" 101 | 102 | //! Declare an explicit instantiation of the default Prgm::Parser 103 | extern template struct Parsers<>; 104 | } // namespace Prgm 105 | } // namespace FLPR 106 | 107 | #undef FLPR_TRACE_PG 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /src/flpr/Token_Text.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file include/flpr/Token_Text.hh 13 | */ 14 | 15 | #ifndef FLPR_TOKEN_TEXT_HH 16 | #define FLPR_TOKEN_TEXT_HH 1 17 | 18 | #include "flpr/Safe_List.hh" 19 | #include "flpr/Syntax_Tags.hh" 20 | #include 21 | #include 22 | 23 | namespace FLPR { 24 | class Logical_Line; 25 | class Logical_File; 26 | 27 | //! A token, and it's corresponding text, as discovered by the lexer. 28 | /*! 29 | This class records some information that you may not find in a compiler: 30 | file positions and spacing information. The file positions, \p 31 | start_line and \p start_pos are the (index 1) positions of the first 32 | character of the lexeme. These are used for error and warning 33 | reporting. The spacing information is used to capture the number of 34 | spaces before and after the lexeme. This is used to recreate the 35 | original input. Keeping the spacing information with the tokens 36 | gives an efficient way of analyzing and editing a sequence of tokens. 37 | */ 38 | class Token_Text { 39 | public: 40 | friend class Logical_Line; 41 | friend class Logical_File; 42 | Token_Text(); 43 | Token_Text(std::string &&txti, int toki, int sli, int spi) 44 | : token(toki), start_line(sli), start_pos(spi), text_(std::move(txti)) {} 45 | 46 | int token; //!< A token identifer from scan_toks.h 47 | int start_line; //!< The file line number of the start of this token 48 | int start_pos; //!< The file character position of the start of this token 49 | 50 | //! Const access to the text 51 | std::string const &text() const noexcept { return text_; } 52 | //! Provide a modifiable reference to text: clears lower_ 53 | std::string &mod_text() noexcept { 54 | lower_.clear(); 55 | return text_; 56 | } 57 | //! Access the lowercase version of text 58 | std::string const &lower() const; 59 | 60 | int pre_spaces() const noexcept { return pre_spaces_; } 61 | int post_spaces() const noexcept { return post_spaces_; } 62 | 63 | /************* Accessors for testing purposes only *******************/ 64 | int main_txt_line() const noexcept { return mt_begin_line_; } 65 | int main_txt_col() const noexcept { return mt_begin_col_; } 66 | int main_txt_eline() const noexcept { return mt_end_line_; } 67 | int main_txt_ecol() const noexcept { return mt_end_col_; } 68 | 69 | private: 70 | std::string text_; //!< The matched text (lexeme) 71 | mutable std::string lower_; //!< Lowercase version of the text 72 | 73 | /*********************************************************************/ 74 | /* For use by Logical_Line friend */ 75 | /*********************************************************************/ 76 | //! Index of the Logical_Line::layout_ line the first character is on 77 | int mt_begin_line_; 78 | //! Index of starting column in main_txt 79 | int mt_begin_col_; 80 | //! Index of the Logical_Line::layout_ line the last+1 character is on 81 | int mt_end_line_; 82 | //! Index of last (+1) column in main_txt 83 | int mt_end_col_; 84 | //! The number of spaces before this token 85 | int pre_spaces_; 86 | //! The number of spaces after this token 87 | int post_spaces_; 88 | /*********************************************************************/ 89 | 90 | private: 91 | constexpr bool is_split_token_() const { 92 | return mt_begin_line_ != mt_end_line_; 93 | } 94 | }; 95 | 96 | //! The type for a sequence of Token_Text 97 | /*! 98 | Note that this container must have the same iterator invalidation 99 | rules as std::list (e.g. iterators remain valid for all sequence 100 | modifications, unless you have an iterator to an element that gets 101 | erased). 102 | */ 103 | using TT_SEQ = FLPR::Safe_List; 104 | //! A range of Token_Text 105 | using TT_Range = FLPR::SL_Range; 106 | 107 | void unkeyword(TT_SEQ::iterator beg, const TT_SEQ::iterator end, 108 | int first_N = -1); 109 | std::ostream &operator<<(std::ostream &os, Token_Text const &tt); 110 | 111 | void render(std::ostream &os, TT_SEQ::const_iterator beg, 112 | TT_SEQ::const_iterator end); 113 | } // namespace FLPR 114 | #endif 115 | -------------------------------------------------------------------------------- /docs/sphinx/conformance.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _conformance: 3 | 4 | ===================== 5 | Standards Conformance 6 | ===================== 7 | 8 | This document discusses the language that FLPR recognizes, and where 9 | there are deficiencies. 10 | 11 | --------------- 12 | Standards Basis 13 | --------------- 14 | 15 | This implementation is based on the 28-DEC-2017 Draft N2146 of the 16 | ISO/IEC JTC1/SC22/WG5 Fortran Language Standard, available from the 17 | `Fortran Working Group `_. As 18 | such, it targets the language of the Fortran 2018 standard. 19 | 20 | ------------------- 21 | Explicit Deviations 22 | ------------------- 23 | 24 | There are several situations where we have elected not to conform with 25 | the Fortran specification: 26 | 27 | 1. The FLPR parser *is sensitive* to whitespace in the input text in 28 | *both* free and fixed source form. This is a deviation from Section 29 | 6.3.3.1.2. If you have very traditional fixed form source code 30 | which does not use spaces to delimit names and keywords, you will need 31 | to preprocess that source code with another tool before processing it 32 | with FLPR. 33 | 2. The FLPR parser does not recognize any nondefault character set. 34 | 3. FLPR does not enforce the constraints of the standard, and thus, is 35 | *not* a standard-conforming processor. See the `Potential 36 | "gotchas"`_ section for an example. 37 | 38 | ------------------- 39 | Retro-Extensions 40 | ------------------- 41 | 42 | Some features that have been deleted or made obsolescent in the 43 | current standard have been implemented to assist modernization 44 | efforts: 45 | 46 | - FORTRAN 77 *kind-selector* (e.g. ``real*8``) 47 | - *computed-goto-stmt* 48 | - *arithmetic-if-stmt* 49 | - *forall-stmt* 50 | - *label-do-stmt* 51 | - *nonblock-do-construct*: FLPR introduces a non-standard 52 | *executable-construct*, called *nonblock-do-construct*, to handle parsing 53 | of F2008 *action-term-do-construct* and *outer-shared-do-construct*. 54 | - The *concurrent-locality* rule for the CONCURRENT clause of *loop-control* 55 | has been made optional to allow *loop-control* to match F2008 *forall-header* 56 | CONCURRENT clauses. 57 | 58 | Note that Hollerith constants are *not* implemented. 59 | 60 | 61 | -------------------------------- 62 | Incomplete Sub-statement Parsing 63 | -------------------------------- 64 | 65 | There are situations where FLPR does not build a completely elaborated 66 | concrete syntax tree for a statement. For example, the syntax rules: 67 | 68 | * *expr* 69 | * *io-control-spec-list* 70 | * *position-spec-list* 71 | 72 | are examples of incomplete parsers in FLPR (there are many). The 73 | expected behavior is that FLPR will indicate a range of tokens that 74 | would be matched by these rules, but present them as a simple sequence 75 | rather than as a tree. 76 | 77 | FLPR is implemented in a top-down fashion, so the user should expect 78 | that FLPR can parse the general structure of a *program*, but the 79 | details of an individual statement may not be fully elaborated. 80 | 81 | 82 | 83 | ------------------ 84 | Known Deficiencies 85 | ------------------ 86 | 87 | There are some parsers that simply have not been implemented yet. The 88 | current list includes: 89 | 90 | * *block-data* 91 | * *change-team-construct* 92 | * *critical-construct* 93 | * *submodule* 94 | 95 | These will be implemented soon, but if one in particular is holding 96 | you up, please file an issue on GitHub. Contributions are always 97 | welcome! 98 | 99 | 100 | 101 | ------------------- 102 | Potential "gotchas" 103 | ------------------- 104 | - FLPR ignores Fortran ``include`` directives and all preprocessor 105 | symbols, so it may get confused if you depend on macro expansion or 106 | file inclusion to produce valid Fortran. Note that there is an 107 | internal FLPR preprocessor, but it only works when directed to. 108 | - FLPR parsers produce *concrete* syntax trees, which include all of 109 | the punctuation and layout information. These are challenging to 110 | extract information from. See the function ``has_call_named()`` in 111 | ``apps/module.cc`` for an example of how to traverse the concrete 112 | syntax tree. Going forward, more abstract representations will be 113 | introduced. 114 | - Note that FLPR enforces the **rules** of the standard, but not the 115 | **constraints**. This will allow FLPR to accept input that will be 116 | rejected by a conforming compiler. For example, rule R1164 defines 117 | *sync-all-stmt* as "SYNC ALL [([ *sync-stat-list* ])]", and 118 | constraint C1171 specifies that a specifier may not appear in the 119 | *sync-stat-list* more than once. FLPR does not enforce that constraint. 120 | - When looking for an *action-stmt* in the input to transform, 121 | remember to look "inside" *if-stmt*, which contain an *action-stmt*! 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /apps/module.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file module.cc 13 | 14 | Demonstrating how to selectively insert a *use-stmt* into 15 | subprograms that contain a *call-stmt* to a particular name. You 16 | may want functionality like this when moving old code into modules. 17 | 18 | A backup copy of the original file contents will be made '.bak' extension, 19 | then the changes will be made under the original file name. 20 | 21 | The code is split up in an unusual way to allow you to easily use the module 22 | operations with your own syntax extensions: just copy this file, add your 23 | extensions in main(), and run. 24 | */ 25 | 26 | #include "module_base.hh" 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using vec_str = std::vector; 33 | 34 | /*--------------------------------------------------------------------------*/ 35 | 36 | void parse_cmd_line(int argc, char *const argv[], vec_str &only_names, 37 | std::string &call_name, bool &call_name_is_file, 38 | std::string &module_name, vec_str &fortran_filenames); 39 | void print_usage(std::ostream &os); 40 | 41 | /*--------------------------------------------------------------------------*/ 42 | 43 | int main(int argc, char *argv[]) { 44 | 45 | vec_str only_names; 46 | vec_str fortran_filenames; 47 | std::string call_name, module_name; 48 | bool call_name_is_file{false}; 49 | 50 | /* only_names is the beginning of support for creating statements like "USE 51 | , ONLY: +". The logic for properly inserting ONLY 52 | names isn't complete, so it isn't wired up to the command line yet */ 53 | parse_cmd_line(argc, argv, only_names, call_name, call_name_is_file, 54 | module_name, fortran_filenames); 55 | 56 | /* You could register FLPR syntax extensions here */ 57 | 58 | FLPR_Module::Module_Action action(std::move(module_name), 59 | std::move(only_names)); 60 | 61 | if (call_name_is_file) { 62 | /* The list of CALL procedure-designators that trigger the USE 63 | insertion is given in a file */ 64 | std::ifstream is(call_name.c_str()); 65 | std::string name; 66 | while (is >> name) { 67 | action.add_subroutine_name(name); 68 | } 69 | is.close(); 70 | } else { 71 | /* There is only one name that triggers the action */ 72 | action.add_subroutine_name(call_name); 73 | } 74 | 75 | for (std::string const &filename : fortran_filenames) { 76 | /* you could change to an alternative file_type_from_ext function here */ 77 | FLPR_Module::do_file(filename, 0, FLPR::file_type_from_extension(filename), 78 | action); 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | /*--------------------------------------------------------------------------*/ 85 | 86 | void parse_cmd_line(int argc, char *const argv[], vec_str &only_names, 87 | std::string &call_name, bool &call_name_is_file, 88 | std::string &module_name, vec_str &fortran_filenames) { 89 | 90 | call_name_is_file = false; 91 | 92 | int ch; 93 | while ((ch = getopt(argc, argv, "f:")) != -1) { 94 | switch (ch) { 95 | case 'f': 96 | call_name = std::string{optarg}; 97 | call_name_is_file = true; 98 | break; 99 | default: 100 | print_usage(std::cerr); 101 | exit(1); 102 | } 103 | } 104 | 105 | int idx = optind; 106 | if (!call_name_is_file) { 107 | if (argc < 4) { 108 | print_usage(std::cerr); 109 | exit(1); 110 | } 111 | call_name = std::string{argv[idx++]}; 112 | } else { 113 | if (argc < 3) { 114 | print_usage(std::cerr); 115 | exit(1); 116 | } 117 | } 118 | 119 | module_name = std::string{argv[idx]}; 120 | for (int i = idx + 1; i < argc; ++i) { 121 | fortran_filenames.emplace_back(std::string{argv[i]}); 122 | } 123 | } 124 | 125 | /*--------------------------------------------------------------------------*/ 126 | 127 | void print_usage(std::ostream &os) { 128 | os << "Usage: module (-f | ) " 129 | " ... \n"; 130 | os << "\t-f \tname of file containing call names\n"; 131 | os << "\t\tthe subroutine name that triggers module addition\n"; 132 | os << "\t\tthe module for which an use-stmt will be added\n"; 133 | os << "\t\tthe Fortran source file to operate on\n"; 134 | } 135 | -------------------------------------------------------------------------------- /docs/flpr-logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 49 | 54 | 55 | 57 | 59 | 60 | 62 | image/svg+xml 63 | 65 | 66 | 67 | 68 | 69 | 74 | 79 | 99 | 104 | FLPR 116 | 117 | 118 | -------------------------------------------------------------------------------- /tests/parse_helpers.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #ifndef PARSE_HELPERS_HH 12 | #define PARSE_HELPERS_HH 1 13 | 14 | #include "LL_Helper.hh" 15 | #include "flpr/Prgm_Parsers.hh" 16 | #include "flpr/Prgm_Tree.hh" 17 | #include "flpr/Syntax_Tags.hh" 18 | #include "test_helpers.hh" 19 | 20 | #include 21 | 22 | using FLPR::Syntax_Tags; 23 | 24 | inline bool expect_tag(Syntax_Tags::Tags const expected_val, int const test_val, 25 | char const *entity_name, char const *filename, 26 | int const linenum) { 27 | if (test_val != expected_val) { 28 | std::cerr << filename << ':' << linenum << " Expecting " << entity_name 29 | << " == \"" << Syntax_Tags::label(expected_val) 30 | << "\", but got \"" << Syntax_Tags::label(test_val) << "\"\n"; 31 | return false; 32 | } 33 | return true; 34 | } 35 | 36 | //! Error out if a tree wasn't returned 37 | #define TEST_TREE(T, P, LLH) \ 38 | if (!T) { \ 39 | std::cerr << "Expecting " #P " to parse:\n"; \ 40 | LLH.print(std::cerr); \ 41 | return false; \ 42 | } 43 | 44 | //! Error out if a tree WAS returned 45 | #define FAIL_TREE(T, P, LLH) \ 46 | if (T.tree_initialized()) { \ 47 | std::cerr << "Expecting " #P " NOT to parse: "; \ 48 | LLH.print(std::cerr); \ 49 | std::cerr << "but got result:\n" << T << '\n'; \ 50 | return false; \ 51 | } 52 | 53 | #define TEST_TAG(A, B) \ 54 | if (!expect_tag(Syntax_Tags::B, A, #A, __FILE__, __LINE__)) \ 55 | return false 56 | 57 | #define TEST_TOK_EQ(A, B, LLH) \ 58 | if ((A) != (B)) { \ 59 | std::cerr << "Expecting " #A " '" << A << "' == " #B " '"; \ 60 | Syntax_Tags::print(std::cerr, B) << "' after parsing\n"; \ 61 | LLH.print(std::cerr); \ 62 | return false; \ 63 | } 64 | 65 | //! Test Single Statement (expects EOL when complete) 66 | #define TSS(SP, S) \ 67 | { \ 68 | LL_Helper l({S}); \ 69 | TT_Stream ts = l.stream1(); \ 70 | Stmt_Tree st = FLPR::Stmt::SP(ts); \ 71 | TEST_TREE(st, SP, l); \ 72 | TEST_TOK_EQ(Syntax_Tags::BAD, ts.peek(), l); \ 73 | } 74 | 75 | //! Fail Single Statement 76 | #define FSS(SP, S) \ 77 | { \ 78 | LL_Helper l({S}); \ 79 | TT_Stream ts = l.stream1(); \ 80 | Stmt_Tree st = FLPR::Stmt::SP(ts); \ 81 | FAIL_TREE(st, SP, l); \ 82 | } 83 | 84 | //! Test Partial Statement (expects token Syntax_Tags::ET when complete) 85 | #define TPS(SP, S, ET) \ 86 | { \ 87 | LL_Helper l({S}); \ 88 | TT_Stream ts = l.stream1(); \ 89 | Stmt_Tree st = FLPR::Stmt::SP(ts); \ 90 | TEST_TREE(st, SP, l); \ 91 | TEST_TOK_EQ(Syntax_Tags::ET, ts.peek(), l); \ 92 | } 93 | 94 | //! Fail Partial Statement (expects token Syntax_Tags::ET when complete) 95 | #define FPS(SP, S, ET) \ 96 | { \ 97 | LL_Helper l({S}); \ 98 | TT_Stream ts = l.stream1(); \ 99 | Stmt_Tree st = FLPR::Stmt::SP(ts); \ 100 | FAIL_TREE(st, SP, l); \ 101 | TEST_TOK_EQ(Syntax_Tags::ET, ts.peek(), l); \ 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /docs/flpr-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 49 | 54 | 55 | 57 | 59 | 60 | 62 | image/svg+xml 63 | 65 | 66 | 67 | 68 | 69 | 74 | 79 | 97 | 104 | 109 | FLPR 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/flpr/Logical_File.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Logical_File.hh 13 | */ 14 | 15 | #ifndef FLPR_LOGICAL_FILE_HH 16 | #define FLPR_LOGICAL_FILE_HH 1 17 | 18 | #include "flpr/File_Info.hh" 19 | #include "flpr/LL_Stmt.hh" 20 | #include "flpr/Logical_Line.hh" 21 | #include "flpr/Safe_List.hh" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace FLPR { 28 | /*! \brief A sequence of Logical_Lines and LL_Stmts that make up a file, plus 29 | other identifying information. */ 30 | class Logical_File { 31 | public: 32 | //! Container for raw text lines of a file 33 | using Line_Buf = std::vector; 34 | //! Type for the container of lines 35 | using const_iterator = typename LL_SEQ::const_iterator; 36 | using iterator = typename LL_SEQ::iterator; 37 | 38 | constexpr Logical_File() : has_flpr_pp{false}, num_input_lines{0} {} 39 | Logical_File(Logical_File &&) = default; 40 | Logical_File(Logical_File const &) = delete; 41 | Logical_File &operator=(Logical_File const &) = delete; 42 | Logical_File &operator=(Logical_File &&) = delete; 43 | ~Logical_File() = default; 44 | 45 | //! Read in the contents of the named file and scan 46 | bool read_and_scan(std::string const &filename, int const last_fixed_col, 47 | File_Type file_type = File_Type::UNKNOWN); 48 | 49 | //! Read in the contents of a stream and scan 50 | bool read_and_scan(std::istream &is, std::string const &stream_name, 51 | int const last_fixed_col, 52 | File_Type file_type = File_Type::UNKNOWN); 53 | 54 | //! Scan a list of raw lines 55 | bool scan(Line_Buf const &line_buffer, std::string const &buffer_name, 56 | int const last_fixed_col, File_Type file_type = File_Type::UNKNOWN); 57 | 58 | //! Scan the file assuming F77-style fixed format 59 | bool scan_fixed(Line_Buf const &fl, int const last_col); 60 | 61 | //! Scan the file assuming F90-style free format 62 | bool scan_free(Line_Buf const &fl); 63 | 64 | File_Type file_type() const { 65 | if (file_info) 66 | return file_info->file_type; 67 | return File_Type::UNKNOWN; 68 | } 69 | bool is_fixed_format() const { return file_type() == File_Type::FIXEDFMT; } 70 | //! Populate ll_stmts: call after lines are loaded 71 | void make_stmts(); 72 | 73 | //! Break a compound into two Logical_Lines before this statement 74 | /*! If pos refers to the first statement of a compound line, nothing happens 75 | and false is returned. Otherwise a new Logical_Line is created following 76 | pos->ll(), pos (and any subsequent compounds) are moved to the new line, 77 | and the Logical_Line and ll_stmts are updated. Note that the ll_stmts are 78 | updated in-place, so any Safe_List iterators to them are still good. */ 79 | bool split_compound_before(LL_STMT_SEQ::iterator pos); 80 | 81 | //! Make sure that this statement is the only one in a Logical_Line 82 | bool isolate_stmt(LL_STMT_SEQ::iterator pos); 83 | 84 | //! emplace a one-stmt Logical_Line before the prefix of statement pos 85 | LL_STMT_SEQ::iterator emplace_ll_stmt(LL_STMT_SEQ::iterator pos, 86 | Logical_Line &&ll, int new_syntag); 87 | 88 | //! emplace a one-stmt Logical_Line after the prefix of statement pos 89 | LL_STMT_SEQ::iterator emplace_ll_stmt_after_prefix(LL_STMT_SEQ::iterator pos, 90 | Logical_Line &&ll, 91 | int new_syntag); 92 | 93 | /* "regen" means re-parse the statement, and fix up ll_stmts */ 94 | 95 | //! Replace all of the text of a statement and regen 96 | void replace_stmt_text(LL_STMT_SEQ::iterator stmt, 97 | std::vector const &new_text, 98 | int new_syntag); 99 | 100 | //! Replace the text covered by orig_tt with new_txt and regen 101 | void replace_stmt_substr(LL_STMT_SEQ::iterator stmt, 102 | LL_TT_Range const &orig_tt, 103 | std::string const &new_txt); 104 | 105 | //! Insert some text after a fragment and regen 106 | void insert_text_after(LL_STMT_SEQ::iterator stmt, TT_SEQ::iterator frag, 107 | std::string const &new_text); 108 | 109 | //! Append some text to the end of stmt and regen 110 | void append_stmt_text(LL_STMT_SEQ::iterator stmt, 111 | std::string const &new_text); 112 | 113 | //! Label a statement (label == 0 will unlabel it) 114 | bool set_stmt_label(LL_STMT_SEQ::iterator stmt, int label); 115 | 116 | //! Convert fixed format to free 117 | bool convert_fixed_to_free(); 118 | 119 | public: 120 | //! Basic information about the input file 121 | std::shared_ptr file_info; 122 | //! The scanned Logical_Lines 123 | LL_SEQ lines; 124 | //! The LL_Stmt's associated with lines 125 | LL_STMT_SEQ ll_stmts; 126 | //! True if read_and_scan found FLPR preprocessor lines 127 | bool has_flpr_pp; 128 | //! Number of scanned line 129 | size_t num_input_lines; 130 | 131 | private: 132 | //! Clear the contents of this structure 133 | void clear(); 134 | }; 135 | 136 | } // namespace FLPR 137 | #endif 138 | -------------------------------------------------------------------------------- /src/flpr/Procedure_Visitor.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file Procedure_Visitor.hh 13 | */ 14 | #ifndef FLPR_PROCEDURE_VISITOR_HH 15 | #define FLPR_PROCEDURE_VISITOR_HH 1 16 | 17 | #include "flpr/Syntax_Tags.hh" 18 | 19 | namespace FLPR { 20 | 21 | //! Calls Action() on every procedure in a Parsed_File 22 | /*! Action needs to look like: 23 | bool(PFile_T &file, Cursor c, bool internal, bool module) 24 | 25 | file: reference to file passed in ctor 26 | c: a cursor to the specific procedure (e.g. c->syntag() will be 27 | SUBROUTINE_SUBPROGRAM, FUNCTION_SUBPROGRAM, MAIN_PROGRAM, 28 | or SEPARATE_MODULE_SUBPROGRAM) 29 | internal: true if c is internal to another procedure 30 | module: true if c is in a module 31 | 32 | the return value will be ||'d together and returned from visit() 33 | */ 34 | template class Procedure_Visitor { 35 | public: 36 | using Cursor = typename PFile_T::Parse_Tree::cursor_t; 37 | 38 | public: 39 | Procedure_Visitor(PFile_T &file, Action &a) : file_{file}, action_{a} {} 40 | bool visit(); 41 | 42 | private: 43 | bool visit_module_(Cursor c, bool const submodule); 44 | bool visit_procedure_(Cursor c, bool const internal, bool const module); 45 | inline Cursor down_copy_(Cursor c) { return c.down(); } 46 | PFile_T &file_; 47 | Action &action_; 48 | }; 49 | 50 | #define TAG(T) FLPR::Syntax_Tags::T 51 | 52 | template 53 | bool Procedure_Visitor::visit() { 54 | bool retval = false; 55 | if (file_.parse_tree().empty()) 56 | return false; 57 | Cursor top_level_cursor = file_.parse_tree().cursor(); 58 | /* This should be a program */ 59 | assert(TAG(PG_PROGRAM) == top_level_cursor->syntag()); 60 | top_level_cursor.down(); 61 | do { 62 | /* These are the program-unit entities. First, we move down to a specific 63 | program-unit. */ 64 | assert(TAG(PG_PROGRAM_UNIT) == top_level_cursor->syntag()); 65 | top_level_cursor.down(); 66 | switch (top_level_cursor->syntag()) { 67 | case TAG(PG_EXTERNAL_SUBPROGRAM): 68 | retval |= visit_procedure_(down_copy_(top_level_cursor), false, false); 69 | break; 70 | case TAG(PG_MODULE): 71 | retval |= visit_module_(top_level_cursor, false); 72 | break; 73 | case TAG(PG_MAIN_PROGRAM): 74 | retval |= visit_procedure_(top_level_cursor, false, false); 75 | break; 76 | case TAG(PG_SUBMODULE): 77 | retval |= visit_module_(top_level_cursor, true); 78 | break; 79 | } 80 | top_level_cursor.up(); 81 | } while (top_level_cursor.try_next()); 82 | return retval; 83 | } 84 | 85 | template 86 | bool Procedure_Visitor::visit_procedure_(Cursor c, 87 | bool const internal, 88 | bool const module) { 89 | /* Call the visit function on this one */ 90 | bool retval = action_(file_, c, internal, module); 91 | 92 | if (internal) // can't have internal subprograms 93 | return retval; 94 | 95 | bool const is_main_program = (TAG(PG_MAIN_PROGRAM) == c->syntag()); 96 | 97 | /* Go see it there are internal subprograms */ 98 | /* c should be on some sort of procedure root */ 99 | c.down(); 100 | if (is_main_program) { 101 | /* the program-stmt is optional */ 102 | if (TAG(SG_PROGRAM_STMT) == c->syntag()) { 103 | c.next(); 104 | } 105 | } else { 106 | // Skip the procedure statement 107 | c.next(); 108 | } 109 | if (TAG(PG_SPECIFICATION_PART) == c->syntag()) { 110 | if (!c.try_next()) 111 | return retval; 112 | } 113 | if (TAG(PG_EXECUTION_PART) == c->syntag()) { 114 | if (!c.try_next()) 115 | return retval; 116 | } 117 | if (TAG(PG_INTERNAL_SUBPROGRAM_PART) == c->syntag()) { 118 | if (!c.try_down()) 119 | return retval; 120 | assert(TAG(SG_CONTAINS_STMT) == c->syntag()); 121 | while (c.try_next()) { // zero or more entries 122 | retval |= visit_procedure_(down_copy_(c), true, module); 123 | }; 124 | } 125 | return retval; 126 | } 127 | 128 | template 129 | bool Procedure_Visitor::visit_module_(Cursor c, 130 | bool const submodule) { 131 | bool retval = false; 132 | /* c should be on a module */ 133 | c.down(); 134 | assert(TAG(SG_MODULE_STMT) == c->syntag()); 135 | c.next(); 136 | if (TAG(PG_SPECIFICATION_PART) == c->syntag()) { 137 | if (!c.try_next()) 138 | return retval; 139 | } 140 | if (TAG(PG_MODULE_SUBPROGRAM_PART) == c->syntag()) { 141 | c.down(); 142 | assert(TAG(SG_CONTAINS_STMT) == c->syntag()); 143 | while (c.try_next()) { // handles case were contains section is empty 144 | c.down(); 145 | if (TAG(PG_FUNCTION_SUBPROGRAM) == c->syntag() || 146 | TAG(PG_SUBROUTINE_SUBPROGRAM) == c->syntag() || 147 | TAG(PG_SEPARATE_MODULE_SUBPROGRAM) == c->syntag()) { 148 | retval |= visit_procedure_(c, false, true); 149 | } 150 | c.up(); 151 | } 152 | } 153 | return retval; 154 | } 155 | 156 | #undef TAG 157 | } // namespace FLPR 158 | #endif 159 | -------------------------------------------------------------------------------- /tests/test_safe_list.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /* 12 | Testing for the Safe_List class 13 | */ 14 | 15 | #include "flpr/Safe_List.hh" 16 | #include "test_helpers.hh" 17 | #include 18 | 19 | using FLPR::Safe_List; 20 | 21 | /* -------------------------- The unit tests ---------------------------- */ 22 | 23 | struct A { 24 | A() : a{-1}, b{-1} {} 25 | A(int a, int b) : a{a}, b{b} {} 26 | int a, b; 27 | }; 28 | 29 | bool ctor_default() { 30 | Safe_List sl; 31 | TEST_TRUE(sl.empty()); 32 | TEST_INT(sl.size(), 0); 33 | return true; 34 | } 35 | 36 | bool ctor_count_val() { 37 | Safe_List sl(6, 6); 38 | TEST_INT(sl.size(), 6); 39 | for (auto e : sl) 40 | TEST_INT(e, 6); 41 | return true; 42 | } 43 | 44 | bool ctor_count() { 45 | Safe_List sl(10); 46 | TEST_INT(sl.size(), 10); 47 | for (auto &e : sl) { 48 | TEST_INT(e.a, -1); 49 | TEST_INT(e.b, -1); 50 | } 51 | 52 | return true; 53 | } 54 | 55 | bool ctor_list() { 56 | Safe_List sl{1, 2, 3}; 57 | TEST_INT(sl.size(), 3); 58 | int i = 1; 59 | for (auto e : sl) 60 | TEST_INT(e, i++); 61 | return true; 62 | } 63 | 64 | const int bigval = 0xa5a5a5a5; 65 | 66 | bool move_ctor_helper1(std::vector> &l); 67 | bool move_ctor_helper2(std::vector> &l); 68 | bool move_ctor() { 69 | /* Note that this doesn't do a good job of identifying the case were 70 | list_ is initialized with {src.list_} rather than {std::move(src.list_)} */ 71 | { 72 | std::vector> l; 73 | TEST_TRUE(move_ctor_helper1(l)); 74 | TEST_INT(l.size(), 1); 75 | TEST_INT(l[0].size(), 4096); 76 | for (auto const &e : l[0]) 77 | TEST_INT(e, bigval); 78 | for (auto &e : l[0]) 79 | e = ~bigval; 80 | for (auto const &e : l[0]) 81 | TEST_INT(e, ~bigval); 82 | TEST_TRUE(move_ctor_helper1(l)); 83 | TEST_INT(l.size(), 2); 84 | TEST_INT(l[0].size(), 4096); 85 | for (auto const &e : l[0]) 86 | TEST_INT(e, ~bigval); 87 | for (auto &e : l[0]) 88 | e = bigval; 89 | for (auto const &e : l[0]) 90 | TEST_INT(e, bigval); 91 | } 92 | { 93 | std::vector> l; 94 | TEST_TRUE(move_ctor_helper2(l)); 95 | TEST_INT(l.size(), 1); 96 | TEST_INT(l[0].size(), 4096); 97 | for (auto const &e : l[0]) 98 | TEST_INT(e, bigval); 99 | for (auto &e : l[0]) 100 | e = ~bigval; 101 | for (auto const &e : l[0]) 102 | TEST_INT(e, ~bigval); 103 | } 104 | return true; 105 | } 106 | 107 | bool push_back() { 108 | Safe_List sl; 109 | 110 | std::string s("a"); 111 | 112 | sl.push_back(s); 113 | TEST_FALSE(sl.empty()); 114 | TEST_INT(sl.size(), 1); 115 | TEST_STR("a", s); 116 | 117 | sl.push_back(std::move(s)); 118 | TEST_FALSE(sl.empty()); 119 | TEST_INT(sl.size(), 2); 120 | 121 | sl.push_back(std::string("a")); 122 | TEST_FALSE(sl.empty()); 123 | TEST_INT(sl.size(), 3); 124 | 125 | for (std::string const &str : sl) 126 | TEST_STR("a", str); 127 | 128 | for (auto const &str : sl) 129 | TEST_STR("a", str); 130 | 131 | return true; 132 | } 133 | 134 | bool emplace_back() { 135 | Safe_List sl; 136 | sl.emplace_back(1, 2); 137 | sl.emplace_back(2, 3); 138 | sl.emplace_back(3, 4); 139 | TEST_INT(sl.size(), 3); 140 | int i = 1; 141 | for (auto &e : sl) { 142 | TEST_INT(e.a, i); 143 | TEST_INT(e.b, i + 1); 144 | i += 1; 145 | } 146 | return true; 147 | } 148 | 149 | bool erase() { 150 | Safe_List sl{1, 2, 3, 4}; 151 | TEST_INT(sl.size(), 4); 152 | sl.erase(std::prev(sl.end())); 153 | TEST_INT(sl.size(), 3); 154 | int i = 1; 155 | for (auto e : sl) 156 | TEST_INT(e, i++); 157 | sl.erase(sl.begin(), sl.end()); 158 | TEST_INT(sl.size(), 0); 159 | TEST_TRUE(sl.empty()); 160 | return true; 161 | } 162 | 163 | bool clear() { 164 | Safe_List sl{1, 2, 3}; 165 | TEST_FALSE(sl.empty()); 166 | TEST_INT(sl.size(), 3); 167 | sl.clear(); 168 | TEST_TRUE(sl.empty()); 169 | TEST_INT(sl.size(), 0); 170 | return true; 171 | } 172 | 173 | bool pop_back() { 174 | Safe_List sl{1, 2, 3}; 175 | TEST_INT(sl.size(), 3); 176 | sl.pop_back(); 177 | TEST_INT(sl.size(), 2); 178 | { 179 | int i = 1; 180 | for (auto e : sl) 181 | TEST_INT(e, i++); 182 | } 183 | sl.pop_back(); 184 | TEST_INT(sl.size(), 1); 185 | { 186 | int i = 1; 187 | for (auto e : sl) 188 | TEST_INT(e, i++); 189 | } 190 | TEST_TRUE(sl.begin() == std::prev(sl.end())); 191 | return true; 192 | } 193 | 194 | int main() { 195 | TEST_MAIN_DECL; 196 | 197 | TEST(ctor_default); 198 | TEST(ctor_count_val); 199 | TEST(ctor_count); 200 | TEST(ctor_list); 201 | TEST(move_ctor); 202 | TEST(push_back); 203 | TEST(emplace_back); 204 | TEST(erase); 205 | TEST(clear); 206 | TEST(pop_back); 207 | 208 | TEST_MAIN_REPORT; 209 | } 210 | 211 | bool move_ctor_helper1(std::vector> &l) { 212 | Safe_List sl(4096, bigval); 213 | TEST_INT(sl.size(), 4096); 214 | l.emplace_back(std::move(sl)); 215 | TEST_INT(l.back().size(), 4096); 216 | sl = Safe_List(); 217 | return true; 218 | } 219 | 220 | bool move_ctor_helper2(std::vector> &l) { 221 | l.emplace_back(Safe_List(4096, bigval)); 222 | TEST_INT(l.back().size(), 4096); 223 | return true; 224 | } 225 | -------------------------------------------------------------------------------- /docs/flpr-logo-card.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 44 | 47 | 48 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 66 | 73 | 74 | 78 | 95 | 102 | 107 | FLPR 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /apps/ext_demo.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /* 12 | Demonstrate how to extend the action-stmt parsers. 13 | */ 14 | 15 | #include "flpr/Logical_File.hh" 16 | #include "flpr/Prgm_Parsers.hh" 17 | #include "flpr/Stmt_Parser_Exts.hh" 18 | #include "flpr/parse_stmt.hh" 19 | #include 20 | 21 | /* Use the default program parsers */ 22 | using Parse = FLPR::Prgm::Parsers<>; 23 | 24 | /* Define a handy shortcut */ 25 | using Parse_Tree = Parse::Prgm_Tree; 26 | 27 | void make_one_line_file(FLPR::Logical_File &lf, char const *const text); 28 | bool parse_executable_construct(FLPR::Logical_File &lf); 29 | namespace FLPR::Stmt { 30 | Stmt_Tree write_comma_stmt(TT_Stream &ts); 31 | Stmt_Tree write_comma_stmt_mytag(TT_Stream &ts); 32 | } // namespace FLPR::Stmt 33 | 34 | // If you want to create your own Syntax_Tags, set them up this way. 35 | enum My_Syntax_Tags { 36 | MY_FOO_STMT = FLPR::Syntax_Tags::CLIENT_EXTENSION, 37 | MY_WRITE_STMT, 38 | MY_BAR_STMT 39 | }; 40 | 41 | int main() { 42 | 43 | /* According to the standard, there should NOT be a comma after the 44 | io-control-spec-list in a write statement. The standard parser accepts a 45 | properly structured write statement... */ 46 | FLPR::Logical_File lf_standard; 47 | make_one_line_file(lf_standard, "write(*,100) a,b"); 48 | std::cout << "Default "; 49 | parse_executable_construct(lf_standard); 50 | 51 | /* ... but does NOT accept a comma after the io-control-spec-list, even though 52 | some compilers do. */ 53 | FLPR::Logical_File lf_comma; 54 | make_one_line_file(lf_comma, "write(*,100), a,b"); 55 | std::cout << "Default "; 56 | parse_executable_construct(lf_comma); 57 | 58 | /* If you have a lot of those kind of commas in your code base, you can create 59 | an action-stmt grammar extension that accepts that format. */ 60 | auto &exts = FLPR::Stmt::get_parser_exts(); 61 | exts.register_action_stmt(FLPR::Stmt::write_comma_stmt); 62 | std::cout << "Extended "; 63 | parse_executable_construct(lf_comma); 64 | 65 | /* The previous rule said that the syntax tag was "SG_WRITE_STMT", but you may 66 | want to give it something unique, in case you need to act on it 67 | specially */ 68 | exts.clear(); 69 | exts.register_action_stmt(FLPR::Stmt::write_comma_stmt_mytag); 70 | 71 | /* If you are printing out the syntax tree, as in this example, you can 72 | register a label and a type (see Syntax_Tags_Defs.hh for a description of 73 | types) that make the output prettier. If you don't do this optional step, 74 | your extension tag will come with a default label of 75 | "", where N is an integer offset from the 76 | Syntax_Tags::CLIENT_EXTENSION tag. Syntax tag extension registration looks 77 | like: */ 78 | FLPR::Syntax_Tags::register_ext(MY_WRITE_STMT, "my-write-stmt", 5); 79 | std::cout << "Extended "; 80 | parse_executable_construct(lf_comma); 81 | 82 | return 0; 83 | } 84 | 85 | void make_one_line_file(FLPR::Logical_File &lf, char const *const text) { 86 | FLPR::Logical_File::Line_Buf buf; 87 | buf.emplace_back(std::string{text}); 88 | lf.scan_free(buf); 89 | } 90 | 91 | bool parse_executable_construct(FLPR::Logical_File &lf) { 92 | lf.make_stmts(); 93 | Parse::State state(lf.ll_stmts); 94 | auto result{Parse::executable_construct(state)}; 95 | if (result.match) { 96 | /* descend down to where we can grab the syntax tag for the write-stmt. We 97 | start in the program tree: executable-construct */ 98 | auto prgm_cursor{result.parse_tree.ccursor()}; 99 | /* descend down executable-construct -> action-stmt */ 100 | prgm_cursor.down(); 101 | /* we're now at a leaf of the program parse tree, so we switch from the 102 | program tree to the statement tree : action-stmt */ 103 | auto stmt_cursor{prgm_cursor->stmt_tree().ccursor()}; 104 | /* descend down action-stmt -> write-stmt */ 105 | stmt_cursor.down(); 106 | 107 | std::cout << "parser recognizes \""; 108 | FLPR::Syntax_Tags::print(std::cout, stmt_cursor->syntag); 109 | std::cout << "\" from \"" << lf.ll_stmts.front() << "\"\n"; 110 | } else { 111 | std::cout << "parser DOES NOT recognize \"" << lf.ll_stmts.front() 112 | << "\"\n"; 113 | } 114 | return result.match; 115 | } 116 | 117 | /* It is much easier to define this in the FLPR::Stmt namespace */ 118 | namespace FLPR::Stmt { 119 | /* An extended write-stmt parser that allows a comma after io-control-spec-list. 120 | This code is a copy of FLPR::Stmt::write_stmt (in parse_stmt.cc) with one 121 | extra line to accept the comma. */ 122 | Stmt_Tree write_comma_stmt(TT_Stream &ts) { 123 | constexpr auto p = seq( 124 | Syntax_Tags::SG_WRITE_STMT, tok(Syntax_Tags::KW_WRITE), 125 | // This fakes the io-control-spec-list 126 | tag_if(Syntax_Tags::SG_IO_CONTROL_SPEC_LIST, rule(consume_parens)), 127 | tok(Syntax_Tags::TK_COMMA), 128 | opt(list(Syntax_Tags::SG_OUTPUT_ITEM_LIST, rule(output_item))), eol()); 129 | return p(ts); 130 | } 131 | 132 | /* Same as above, but we're going to tag it with a client-extension syntax 133 | tag */ 134 | Stmt_Tree write_comma_stmt_mytag(TT_Stream &ts) { 135 | constexpr auto p = seq( 136 | MY_WRITE_STMT, tok(Syntax_Tags::KW_WRITE), 137 | // This fakes the io-control-spec-list 138 | tag_if(Syntax_Tags::SG_IO_CONTROL_SPEC_LIST, rule(consume_parens)), 139 | tok(Syntax_Tags::TK_COMMA), 140 | opt(list(Syntax_Tags::SG_OUTPUT_ITEM_LIST, rule(output_item))), eol()); 141 | return p(ts); 142 | } 143 | 144 | } // namespace FLPR::Stmt 145 | -------------------------------------------------------------------------------- /src/flpr/LL_Stmt.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file LL_Stmt.hh 13 | */ 14 | 15 | #ifndef FLPR_LL_STMT_HH 16 | #define FLPR_LL_STMT_HH 1 17 | 18 | #include "flpr/LL_TT_Range.hh" 19 | #include "flpr/Safe_List.hh" 20 | #include "flpr/Stmt_Tree.hh" 21 | #include 22 | 23 | namespace FLPR { 24 | //! Identify a LL_TT_Range that describes a Fortran statement 25 | class LL_Stmt : public LL_TT_Range { 26 | public: 27 | using Stmt_Tree = FLPR::Stmt::Stmt_Tree; 28 | 29 | public: 30 | LL_Stmt() 31 | : LL_TT_Range(), label_{0}, compound_{-1}, hook_{nullptr}, 32 | stmt_syntag_{Syntax_Tags::UNKNOWN} {}; 33 | LL_Stmt(LL_IT line_ref, TT_Range r, int label, int compound) 34 | : LL_TT_Range(line_ref, r), label_{label}, compound_{compound}, 35 | hook_{nullptr}, stmt_syntag_{Syntax_Tags::UNKNOWN} {} 36 | 37 | void update_range(LL_Stmt &&src) { 38 | LL_TT_Range::operator=(src); 39 | compound_ = src.compound_; 40 | label_ = src.label_; 41 | stmt_tree_.clear(); // It is bad at this point 42 | } 43 | 44 | constexpr bool has_label() const { return label_ > 0; } 45 | constexpr int label() const { return label_; } 46 | //! Note that this does NOT update the label on ll()!!!! 47 | constexpr void cache_new_label_value(int newval) { label_ = newval; } 48 | 49 | //! See commentary on compound_ member 50 | constexpr void set_compound(int newval) { compound_ = newval; } 51 | constexpr int is_compound() const { return compound_; } 52 | 53 | std::ostream &print_me(std::ostream &, bool const print_prefix = true) const; 54 | virtual std::ostream &print(std::ostream &os) const override { 55 | return print_me(os, true); 56 | } 57 | constexpr bool equal(LL_Stmt const &s) const { 58 | return LL_TT_Range::equal(s) && label_ == s.label_ && 59 | compound_ == s.compound_; 60 | } 61 | constexpr void set_hook(void *ptr) noexcept { hook_ = ptr; } 62 | constexpr void unhook() noexcept { hook_ = nullptr; } 63 | constexpr void *get_hook() const { return hook_; } 64 | constexpr bool has_hook() const noexcept { return hook_ != nullptr; } 65 | 66 | //! Re-indent the prefix lines and statement 67 | /*! 68 | Returns true if any spacing was modified, false otherwise. 69 | \param[in] spaces the starting column (index-0) of the statement 70 | \param[in] continued_offset the additional offset for continued lines 71 | */ 72 | bool set_leading_spaces(int const spaces, int const continued_offset); 73 | int get_leading_spaces() const noexcept { return ll().get_leading_spaces(); } 74 | 75 | Stmt_Tree const &stmt_tree() const noexcept { 76 | if (stmt_tree_.empty()) { 77 | bool res = rebuild_tree_(); 78 | assert(res); 79 | } 80 | return stmt_tree_; 81 | } 82 | Stmt_Tree &stmt_tree() noexcept { 83 | if (stmt_tree_.empty()) { 84 | bool res = rebuild_tree_(); 85 | assert(res); 86 | } 87 | return stmt_tree_; 88 | } 89 | void set_stmt_tree(Stmt_Tree &&stmt_tree) { 90 | stmt_tree_ = std::move(stmt_tree); 91 | extract_tree_tag_(); 92 | } 93 | void drop_stmt_tree() { stmt_tree_.clear(); } 94 | void reset_stmt_tree() { 95 | stmt_tree_.clear(); 96 | extract_tree_tag_(); 97 | } 98 | void set_stmt_syntag(int syntag) { 99 | /* This overrules anything in the tree */ 100 | if (syntag != stmt_syntag_) { 101 | stmt_tree_.clear(); 102 | } 103 | stmt_syntag_ = syntag; 104 | } 105 | 106 | //! Produce a meaningful tag for statements, BAD otherwise 107 | int stmt_tag(bool look_inside_if_stmt) const; 108 | 109 | size_t prefix_size() const { return prefix_lines.size(); } 110 | LL_IT prefix_ll_begin() const { 111 | if (prefix_lines.empty()) { 112 | return it(); 113 | } 114 | return prefix_lines.front(); 115 | } 116 | LL_IT prefix_ll_end() const { 117 | if (prefix_lines.empty()) { 118 | return it(); 119 | } 120 | /* This should be the same as stmt_ll(), unless we're moving the prefix from 121 | one LL_Stmt to another */ 122 | return std::next(prefix_lines.back()); 123 | } 124 | LL_IT stmt_ll() const { return it(); } 125 | constexpr int syntax_tag() const { return stmt_syntag_; } 126 | 127 | public: 128 | //! A sequence of non-Fortran Logical_Lines before this statement. 129 | /*! 130 | These are attached to a statement so that, as a list of statements 131 | gets reordered, the comments and macros move with the statements. 132 | */ 133 | using LL_IT_SEQ = std::vector; 134 | LL_IT_SEQ prefix_lines; 135 | 136 | private: 137 | int label_; // = 0 -> no label. Needs to sync with Logical_Line 138 | //! Statement sequence number in a compound logical line 139 | /*! 0 -> single statement, 140 | 1, 2, ... -> order of this statement in a compound line 141 | */ 142 | int compound_; 143 | 144 | /* This is used to allow Prgm_Tree to install a generic uplink to a Tree node. 145 | We don't know what the template parameter on Prgm_Tree is going to be, so 146 | we use void* instead. */ 147 | void *hook_; 148 | mutable Stmt_Tree stmt_tree_; 149 | mutable int stmt_syntag_; 150 | 151 | private: 152 | void extract_tree_tag_() const { 153 | if (stmt_tree_.empty()) 154 | stmt_syntag_ = Syntax_Tags::UNKNOWN; 155 | else { 156 | auto c = stmt_tree_.ccursor(); 157 | if (c->syntag == Syntax_Tags::SG_ACTION_STMT) 158 | c.down(); 159 | stmt_syntag_ = c->syntag; 160 | } 161 | } 162 | bool rebuild_tree_() const; 163 | }; 164 | 165 | //! Container for a sequence of LL_Stmts 166 | using LL_STMT_SEQ = Safe_List; 167 | 168 | inline std::ostream &operator<<(std::ostream &os, LL_Stmt const &s) { 169 | return s.print_me(os, true); 170 | } 171 | 172 | } // namespace FLPR 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /apps/module_base.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | /*! 12 | \file module_base.cc 13 | 14 | Demonstrating how to selectively insert a *use-stmt* into 15 | subprograms that contain a *call-stmt* to a particular name. You 16 | may want functionality like this when moving old code into modules. 17 | */ 18 | 19 | #include "module_base.hh" 20 | #include 21 | #include 22 | 23 | #define TAG(X) FLPR::Syntax_Tags::X 24 | 25 | namespace FLPR_Module { 26 | 27 | /*--------------------------------------------------------------------------*/ 28 | 29 | bool do_file(std::string const &filename, int const last_fixed_col, 30 | FLPR::File_Type file_type, Module_Action const &visit_action) { 31 | 32 | File file(filename, last_fixed_col, file_type); 33 | if (!file) 34 | exit(1); 35 | 36 | FLPR::Procedure_Visitor puv(file, visit_action); 37 | 38 | bool const changed = puv.visit(); 39 | 40 | if (changed) { 41 | std::string bak{filename + ".bak"}; 42 | if (!rename(filename.c_str(), bak.c_str())) { 43 | std::ofstream os(filename); 44 | write_file(os, file); 45 | os.close(); 46 | } else { 47 | std::cerr << "Unable to rename \"" << filename << "\" to \"" << bak 48 | << std::endl; 49 | exit(2); 50 | } 51 | } 52 | return changed; 53 | } 54 | 55 | /*--------------------------------------------------------------------------*/ 56 | 57 | bool Module_Action::operator()(File &file, Cursor c, 58 | bool const internal_procedure, 59 | bool const module_procedure) const { 60 | if (internal_procedure) { 61 | return false; 62 | } 63 | 64 | Procedure proc(file); 65 | if (!proc.ingest(c)) { 66 | std::cerr << "\n******** Unable to ingest procedure *******\n" << std::endl; 67 | return false; 68 | } 69 | 70 | if (!proc.has_region(Procedure::EXECUTION_PART)) { 71 | std::cerr << "skipping " << proc.name() << ": no execution part" 72 | << std::endl; 73 | return false; 74 | } 75 | 76 | { 77 | auto execution_part{proc.crange(Procedure::EXECUTION_PART)}; 78 | bool found{false}; 79 | for (auto const &stmt : execution_part) { 80 | if (has_call_named(stmt, subroutine_names_)) { 81 | found = true; 82 | break; 83 | } 84 | } 85 | if (!found) 86 | return false; 87 | } 88 | 89 | auto use_stmts{proc.range(Procedure::USES)}; 90 | bool found{false}; 91 | for (auto &stmt : use_stmts) { 92 | Stmt_Cursor use_c = find_use_module_name(stmt); 93 | assert(use_c->token_range.size() == 1); 94 | if (use_c->token_range.front().lower() == module_lc_) 95 | found = true; 96 | } 97 | 98 | if (!found) { 99 | auto use_it = proc.emplace_stmt(proc.end(Procedure::USES), 100 | FLPR::Logical_Line{"use " + module_name_}, 101 | TAG(SG_USE_STMT), false); 102 | use_it->set_leading_spaces(std::next(use_it)->get_leading_spaces(), 2); 103 | } 104 | 105 | return true; 106 | } 107 | 108 | /*--------------------------------------------------------------------------*/ 109 | 110 | bool has_call_named(FLPR::LL_Stmt const &stmt, 111 | std::unordered_set const &lowercase_names) { 112 | int const stmt_tag = stmt.syntax_tag(); 113 | if (TAG(SG_CALL_STMT) != stmt_tag && TAG(SG_IF_STMT) != stmt_tag) 114 | return false; 115 | 116 | auto c{stmt.stmt_tree().ccursor()}; 117 | 118 | /* If we are on an if-stmt, we need to move over to the action-stmt */ 119 | if (TAG(SG_IF_STMT) == stmt_tag) { 120 | assert(TAG(SG_ACTION_STMT) == c->syntag); 121 | c.down(); 122 | assert(TAG(SG_IF_STMT) == c->syntag); 123 | c.down(); 124 | assert(TAG(KW_IF) == c->syntag); 125 | c.next(4); 126 | } 127 | 128 | /* Enter the action-stmt */ 129 | assert(TAG(SG_ACTION_STMT) == c->syntag); 130 | c.down(1); 131 | 132 | /* If it is not a call-stmt, return */ 133 | if (TAG(SG_CALL_STMT) != c->syntag) 134 | return false; 135 | 136 | /* Enter the call-stmt */ 137 | c.down(1); 138 | 139 | /* First, we should have a CALL keyword */ 140 | assert(TAG(KW_CALL) == c->syntag); 141 | c.next(1); 142 | 143 | /* Then, a procedure-designator, which we will enter */ 144 | assert(TAG(SG_PROCEDURE_DESIGNATOR) == c->syntag); 145 | c.down(); 146 | 147 | /* See if we have a "fancy" procedure-designator */ 148 | if (TAG(SG_PART_REF) == c->syntag) { 149 | /* This is some elaborated name (proc-component-ref or data-ref % 150 | binding-name), so we move to the last item in the list, which should be a 151 | name) */ 152 | while (c.has_next()) 153 | c.next(1); 154 | } 155 | 156 | /* Now, we are on the procedure-name, the procedure-component-name, or the 157 | binding-name (depending on the type of procedure-designator). See if it is 158 | listed in our set of matchers */ 159 | assert(TAG(TK_NAME) == c->syntag); 160 | std::string lname = c->token_range.front().lower(); 161 | return (lowercase_names.count(lname) == 1); 162 | } 163 | 164 | Stmt_Cursor find_use_module_name(FLPR::LL_Stmt &stmt) { 165 | Stmt_Cursor c{stmt.stmt_tree().cursor()}; 166 | assert(TAG(SG_USE_STMT) == c->syntag); 167 | c.down(1); 168 | assert(TAG(KW_USE) == c->syntag); 169 | c.next(1); 170 | if (TAG(TK_COMMA) == c->syntag) 171 | c.next(2); 172 | if (TAG(TK_DBL_COLON) == c->syntag) 173 | c.next(1); 174 | assert(TAG(TK_NAME) == c->syntag); 175 | return c; 176 | } 177 | 178 | /*--------------------------------------------------------------------------*/ 179 | 180 | void write_file(std::ostream &os, File const &f) { 181 | for (auto const &ll : f.logical_lines()) { 182 | os << ll; 183 | } 184 | } 185 | 186 | } // namespace FLPR_Module 187 | -------------------------------------------------------------------------------- /tests/test_tt_stream.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2020, Triad National Security, LLC. All rights reserved. 3 | 4 | This is open source software; you can redistribute it and/or modify it 5 | under the terms of the BSD-3 License. If software is modified to produce 6 | derivative works, such modified software should be clearly marked, so as 7 | not to confuse it with the version available from LANL. Full text of the 8 | BSD-3 License can be found in the LICENSE file of the repository. 9 | */ 10 | 11 | #include "LL_Helper.hh" 12 | #include "test_helpers.hh" 13 | 14 | using namespace FLPR; 15 | 16 | bool test_chop_at_open_paren() { 17 | /* This is less of a unit test, and more a test of a strange pattern used 18 | in the procedure-designator parser. */ 19 | { 20 | LL_Helper l({"call foo(bar)"}); 21 | TT_Stream ts = l.stream1(); 22 | 23 | TEST_EQ(ts.peek(), Syntax_Tags::KW_CALL); 24 | ts.consume(); 25 | TEST_EQ(ts.peek(), Syntax_Tags::TK_NAME); 26 | 27 | /* Hold this location */ 28 | auto designator = ts.capture_begin(); 29 | ts.consume_until_eol(); 30 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENR); 31 | bool move_okay = ts.move_to_open_paren(); 32 | TEST_TRUE(move_okay); 33 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENL); 34 | 35 | /* Hold this location */ 36 | ts.put_back(); 37 | ts.capture_end(designator); 38 | 39 | TT_Stream ts2(ts.capture_to_range(designator)); 40 | TEST_EQ(ts2.peek(), Syntax_Tags::TK_NAME); 41 | ts2.consume(); 42 | TEST_TRUE(ts2.is_eol()); 43 | 44 | TEST_EQ(ts.peek(1), Syntax_Tags::TK_PARENL); 45 | TEST_EQ(ts.peek(2), Syntax_Tags::TK_NAME); 46 | TEST_EQ(ts.peek(3), Syntax_Tags::TK_PARENR); 47 | ts.consume(3); 48 | TEST_TRUE(ts.is_eol()); 49 | } 50 | 51 | { 52 | LL_Helper l({"call foo"}); 53 | TT_Stream ts = l.stream1(); 54 | 55 | TEST_EQ(ts.peek(), Syntax_Tags::KW_CALL); 56 | ts.consume(); 57 | TEST_EQ(ts.peek(), Syntax_Tags::TK_NAME); 58 | 59 | /* Hold this location */ 60 | auto designator = ts.capture_begin(); 61 | ts.consume_until_eol(); 62 | TEST_EQ(ts.curr(), Syntax_Tags::TK_NAME); 63 | bool move_okay = ts.move_to_open_paren(); 64 | TEST_FALSE(move_okay); 65 | 66 | /* Hold this location */ 67 | if (move_okay) 68 | ts.put_back(); 69 | ts.capture_end(designator); 70 | 71 | TT_Stream ts2(ts.capture_to_range(designator)); 72 | TEST_EQ(ts2.peek(), Syntax_Tags::TK_NAME); 73 | ts2.consume(); 74 | TEST_STR("foo", ts2.curr_tt().text()); 75 | TEST_TRUE(ts2.is_eol()); 76 | 77 | TEST_TRUE(ts.is_eol()); 78 | } 79 | 80 | return true; 81 | } 82 | 83 | bool test_consume_until_eol() { 84 | { 85 | LL_Helper l({"(this is a test)"}); 86 | TT_Stream ts = l.stream1(); 87 | ts.consume_until_eol(); 88 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENR); 89 | } 90 | return true; 91 | } 92 | 93 | bool test_move_to_close_paren() { 94 | { 95 | LL_Helper l({"(test(()))="}); 96 | TT_Stream ts = l.stream1(); 97 | ts.move_to_close_paren(); 98 | TEST_EQ(ts.peek(), Syntax_Tags::TK_EQUAL); 99 | } 100 | { 101 | LL_Helper l({"(test(())="}); 102 | TT_Stream ts = l.stream1(); 103 | ts.move_to_close_paren(); 104 | TEST_EQ(ts.peek(), Syntax_Tags::BAD); 105 | } 106 | return true; 107 | } 108 | 109 | bool test_move_to_open_paren() { 110 | { 111 | LL_Helper l({"a"}); 112 | TT_Stream ts = l.stream1(); 113 | ts.consume_until_eol(); 114 | bool move_okay = ts.move_to_open_paren(); 115 | TEST_FALSE(move_okay); 116 | } 117 | { 118 | LL_Helper l({"a)"}); 119 | TT_Stream ts = l.stream1(); 120 | ts.consume_until_eol(); 121 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENR); 122 | bool move_okay = ts.move_to_open_paren(); 123 | TEST_FALSE(move_okay); 124 | } 125 | { 126 | LL_Helper l({"a()"}); 127 | TT_Stream ts = l.stream1(); 128 | ts.consume_until_eol(); 129 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENR); 130 | bool move_okay = ts.move_to_open_paren(); 131 | TEST_TRUE(move_okay); 132 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENL); 133 | /* because move_to_open_paren makes curr() == TK_PARENL, it is as if the 134 | TK_PARENL token was consumed, so we get a value of 2, rather than 135 | position == 1. */ 136 | TEST_INT(ts.num_consumed(), 2); 137 | } 138 | { 139 | LL_Helper l({"a(())"}); 140 | TT_Stream ts = l.stream1(); 141 | ts.consume_until_eol(); 142 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENR); 143 | bool move_okay = ts.move_to_open_paren(); 144 | TEST_TRUE(move_okay); 145 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENL); 146 | /* because move_to_open_paren makes curr() == TK_PARENL, it is as if the 147 | TK_PARENL token was consumed, so we get a value of 2, rather than 148 | position == 1. */ 149 | TEST_INT(ts.num_consumed(), 2); 150 | } 151 | { 152 | LL_Helper l({"a(b(),c(d()))"}); 153 | TT_Stream ts = l.stream1(); 154 | ts.consume_until_eol(); 155 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENR); 156 | bool move_okay = ts.move_to_open_paren(); 157 | TEST_TRUE(move_okay); 158 | TEST_EQ(ts.curr(), Syntax_Tags::TK_PARENL); 159 | /* because move_to_open_paren makes curr() == TK_PARENL, it is as if the 160 | TK_PARENL token was consumed, so we get a value of 2, rather than 161 | position == 1. */ 162 | TEST_INT(ts.num_consumed(), 2); 163 | } 164 | 165 | return true; 166 | } 167 | 168 | bool test_move_before_close_paren() { 169 | { 170 | LL_Helper l({"(test(()))="}); 171 | TT_Stream ts = l.stream1(); 172 | ts.move_before_close_paren(); 173 | TEST_EQ(ts.peek(1), Syntax_Tags::TK_PARENR); 174 | TEST_EQ(ts.peek(2), Syntax_Tags::TK_EQUAL); 175 | } 176 | { 177 | LL_Helper l({"(test(())="}); 178 | TT_Stream ts = l.stream1(); 179 | ts.move_before_close_paren(); 180 | TEST_EQ(ts.peek(), Syntax_Tags::BAD); 181 | } 182 | return true; 183 | } 184 | 185 | int main() { 186 | TEST_MAIN_DECL; 187 | TEST(test_consume_until_eol); 188 | TEST(test_move_to_close_paren); 189 | TEST(test_move_to_open_paren); 190 | TEST(test_move_before_close_paren); 191 | 192 | TEST(test_chop_at_open_paren); 193 | TEST_MAIN_REPORT; 194 | } 195 | --------------------------------------------------------------------------------