Skip to content

Commit

Permalink
#144 Add interactive command line debugger support
Browse files Browse the repository at this point in the history
WIP
  • Loading branch information
cmannett85 committed Mar 7, 2021
1 parent a3216af commit c6e1272
Show file tree
Hide file tree
Showing 26 changed files with 1,344 additions and 41 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ set(HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/normalise.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/traits.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/argument_parser.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/file_load.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/from_chars.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/raii.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/signal.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/stream_helpers.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/string_constant.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/string_view_ops.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/utility/tuple_iterator.hpp
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ A coverage report of the latest master commit is available [here](https://cmanne
* Boost v1.71
* CMake v3.12
* Emscripten v2.0.10 (only needed for WASM build)
* Doxygen (only needed for Documentation build)
* Doxygen v1.8.18 (only needed for Documentation build)

---

Expand Down
20 changes: 19 additions & 1 deletion cmake/build_types/standard_executable.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,24 @@
# See LICENSE file
#

add_executable(malbolge ${MAIN_SRCS})
set(CURSES_NEED_NCURSES ON)
find_package(Curses REQUIRED)

set(TERMINAL_GUI_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/ui/terminal/core.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/ui/terminal/layout.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/ui/terminal/pane.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/malbolge/ui/terminal/program_pane.hpp
)

set(TERMINAL_GUI_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/src/ui/terminal/core.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ui/terminal/layout.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ui/terminal/pane.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ui/terminal/program_pane.cpp
)

add_executable(malbolge ${MAIN_SRCS} ${TERMINAL_GUI_HEADERS} ${TERMINAL_GUI_SRCS})

# The documentation is built so we can add the API version of the README to the
# installation packages, this is needed as the 'base' README.md has external
Expand All @@ -28,6 +45,7 @@ target_include_directories(malbolge
target_link_libraries(malbolge
PUBLIC Threads::Threads
PUBLIC malbolge_lib
PUBLIC ${CURSES_LIBRARIES}
)

install(TARGETS malbolge
Expand Down
2 changes: 2 additions & 0 deletions cmake/build_types/unit_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ set(TEST_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/source_location_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/traits_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/argument_parser_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/file_load_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/from_chars_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/raii_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/signal_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/stream_helpers_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/string_constant_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/string_view_ops_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utility/tuple_iterator_test.cpp
Expand Down
1 change: 1 addition & 0 deletions include/malbolge/loader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ virtual_memory load(R&& range,

/** Loads the program data read from @a path.
*
* @note There is no software defined file size limit
* @param path Path to text file containing the program
* @param mode Program load normalised mode
* @return Virtual memory image with the program at the start
Expand Down
50 changes: 50 additions & 0 deletions include/malbolge/traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ template <auto Value>
using integral_constant = std::integral_constant<std::decay_t<decltype(Value)>,
Value>;

/** Converts an enum to its underlying integer type whilst retaining its value.
*
* @tparam Enum Enum type
* @param e Instance to convert
*/
template <typename Enum>
constexpr auto to_underlying_type(Enum e)
{
static_assert(std::is_enum_v<Enum>, "e must be an enum type");
return static_cast<std::underlying_type_t<Enum>>(e);
}

/** Evaluates to true if @a T is a tuple-like type.
*
* A tuple-like type is one that is can be used with std::tuple_size (i.e.
Expand Down Expand Up @@ -117,5 +129,43 @@ constexpr auto arg_extractor_impl(const T&) -> std::tuple<>
*/
template <typename T>
using arg_extractor = std::decay_t<decltype(detail::arg_extractor_impl(T{}))>;

namespace detail
{
template <template <class...> typename Trait, typename AlwaysVoid, typename... Args>
struct is_detected_impl : std::false_type
{};

template <template <class...> typename Trait, typename... Args>
struct is_detected_impl<Trait, std::void_t<Trait<Args...>>, Args...> : std::true_type
{};
}

/** Alias for determining if a trait is valid with @a T as its template
* argument.
*
* This is primarily used for detecting if a type has a member of supports an
* expression with an operator:
* @code
* template <typename T>
* using reserve_checker = decltype(std::declval<T&>().reserve(std::declval<std::size_t>()));
*
* is_detected<reserve_checker, std::vector<char>>; // Alias for std::true_type
* is_detected<reserve_checker, std::array<char, 4>>; // Alias for std::false_type
* @endcode
* @note The constness of @a T is preserved
* @tparam Trait Trait to test with @a T
* @tparam T Type to test against @a Trait
*/
template <template <class...> typename Trait, typename T>
using is_detected = detail::is_detected_impl<Trait, void, T>;

/** Helper variable for is_detected.
*
* @tparam Trait Trait to test with @a T
* @tparam T Type to test against @a Trait
*/
template <template <class...> typename Trait, typename T>
constexpr bool is_detected_v = is_detected<Trait, T>::value;
}
}
53 changes: 53 additions & 0 deletions include/malbolge/ui/terminal/core.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* Cam Mannett 2021
*
* See LICENSE file
*/

#pragma once

#include "malbolge/utility/argument_parser.hpp"

namespace malbolge
{
/** Namespace for all GUI types and functions.
*/
namespace ui
{
/** Namespace for an interactive terminal GUI, based on ncurses.
*/
namespace terminal
{
/** Core terminal functionality.
*
* This class can be moved, but not copied.
*/
class core
{
public:
/** Constructor.
*
* @param program_data Program data
*/
explicit core(argument_parser::program_data program_data);

core(const core&) = delete;
core& operator=(const core&) = delete;
core(core&&) noexcept = default;
core& operator=(core&&) noexcept = delete;

/** Runs the interactive terminal UI.
*
* This function blocks until the UI is exited, which should signal the
* application to exit.
* @exception basic_exception Thrown if called on a moved-from instance
*/
void run();

private:
// We use PIMPL to minimise the amount of TUs that import the ncurses header
struct impl_t;
std::shared_ptr<impl_t> impl_;
};
}
}
}
110 changes: 110 additions & 0 deletions include/malbolge/ui/terminal/layout.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* Cam Mannett 2021
*
* See LICENSE file
*/

#pragma once

#include <memory>
#include <optional>

namespace malbolge
{
namespace ui
{
namespace terminal
{
class pane;

/** Manages the layout of panes inside the terminal.
*
* This class can be moved but not copied.
*/
class layout
{
public:
/** Alias for an owning pointer.
*/
using pane_ptr = std::shared_ptr<pane>;

/** The direction to split a pane into two.
*/
enum class split_direction {
HORIZONTAL,
VERTICAL
};

/** Constructor.
*
* @param lines Number of lines in the whole terminal
* @param cols Number of columns in the whole terminal
*/
explicit layout(std::size_t lines, std::size_t cols);

/** Replace the existing pane (or empty root) with @a new_pane.
*
* @param new_pane New pane
* @param existing_pane An existing pane to replace, or null to replace the
* root
* @exception basic_exception Thrown if @a new_pane is null, or is
* @a existing_pane is non-null but cannot be found in the layout
*/
void replace(pane_ptr new_pane, pane_ptr existing_pane = {});

/** Split @a parent and add @a new_pane to the opened slot.
*
* The parent of a split is either the top or left pane depending on @a dir.
* @param new_pane New pane
* @param dir Direction to split @a parent
* @param parent Pane to split
* @exception basic_exception Thrown if @a new_pane or @a parent is null, or
* if @a parent cannot be found in the layout
* @exception basic_exception Thrown if @a parent cannot be split anymore
*/
void split(pane_ptr new_pane, split_direction dir, pane_ptr parent);

/** Inform the layout engine that the terminal has been resized.
*
* This will trigger a redraw of all child panes.
* @param lines New line count
* @param cols New column count
*/
void terminal_resize(std::size_t lines, std::size_t cols);

/** Trigger a redraw of all the panes in the layout.
*
* This should be called after the layout has been modified.
*/
void refresh() noexcept
{
refresh(root_);
}

layout(layout&&) = default;
layout& operator=(layout&&) = default;
layout(const layout&) = delete;
layout& operator=(const layout&) = delete;

private:
struct node;
using node_ptr = std::shared_ptr<node>;

void recalculate_dimensions(node_ptr start_node);
void refresh(node_ptr start_node) noexcept;

[[nodiscard]]
node_ptr find(std::shared_ptr<pane> target, node_ptr start_node) noexcept;

[[nodiscard]]
node_ptr find(std::shared_ptr<pane> target) noexcept
{
return find(std::move(target), root_);
}

std::size_t lines_;
std::size_t cols_;
node_ptr root_;
};
}
}
}
Loading

0 comments on commit c6e1272

Please sign in to comment.