diff options
Diffstat (limited to 'ttwhy/scanner.cppm')
| -rw-r--r-- | ttwhy/scanner.cppm | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/ttwhy/scanner.cppm b/ttwhy/scanner.cppm new file mode 100644 index 0000000..86a8493 --- /dev/null +++ b/ttwhy/scanner.cppm @@ -0,0 +1,127 @@ +module; + +#include <algorithm> +#include <boost/sml.hpp> + +#include <cctype> +#include <span> +#include <vector> + +export module ttwhy:scanner; + +import :event; + +namespace ttwhy::detail +{ + + /// Events + + struct byte_received + { + char value; + }; + + /// States + + auto const idle = boost::sml::state<class idle>; + auto const escape_sequence = boost::sml::state<class escape_sequence>; + auto const csi_sequence = boost::sml::state<class csi_sequence>; + + /// Actions + + auto push_character = [](byte_received const & event, std::vector<input_event> & queue) { + queue.push_back(character_event{event.value}); + }; + + auto push_backspace = [](std::vector<input_event> & queue) { + queue.push_back(control_event{control_key::backspace}); + }; + + auto push_delete = [](std::vector<input_event> & queue) { + queue.push_back(navigation_event{navigation_key::delete_key}); + }; + + auto fallback_escape = [](byte_received const & event, std::vector<input_event> & queue) { + queue.push_back(control_event{control_key::escape}); + if (event.value >= 0x20 && event.value <= 0x7e) + { + queue.push_back(character_event{event.value}); + } + }; + + /// Guards + + auto is_backspace = [](byte_received e) { + return e.value == '\x08' || e.value == '\x7f'; + }; + + auto is_escape = [](byte_received e) { + return e.value == '\x1b'; + }; + + auto is_printable = [](byte_received e) { + return e.value >= 0x20 && e.value <= 0x7e; + }; + + auto is_csi_introducer = [](byte_received e) { + return e.value == '['; + }; + + auto is_csi_param = [](byte_received e) { + return e.value >= 0x20 && e.value <= 0x3f; + }; + + auto is_csi_terminator = [](byte_received e) { + return e.value >= 0x40 && e.value <= 0x7e; + }; + + auto is_tilde = [](byte_received e) { + return e.value == '~'; + }; + + /// Transitions + + struct transition_table + { + auto operator()() const noexcept + { + using namespace boost::sml; + + // clang-format off + return make_transition_table( + *idle + event<byte_received>[is_escape] = escape_sequence, + idle + event<byte_received>[is_backspace] / push_backspace = idle, + idle + event<byte_received>[is_printable] / push_character = idle, + + escape_sequence + event<byte_received>[is_csi_introducer] = csi_sequence, + escape_sequence + event<byte_received>[!is_csi_introducer] / fallback_escape = idle, + + csi_sequence + event<byte_received>[is_csi_param] = csi_sequence, + csi_sequence + event<byte_received>[is_tilde] / push_delete = idle, + csi_sequence + event<byte_received>[is_csi_terminator] = idle + ); + // clang-format on + } + }; + +} // namespace ttwhy::detail + +export namespace ttwhy +{ + + struct ansi_scanner + { + explicit ansi_scanner(std::vector<input_event> & queue) + : m_state_machine{queue} + {} + + auto process(std::span<char const> buffer) -> void + { + std::ranges::for_each(buffer, [&](auto byte) { m_state_machine.process_event(detail::byte_received{byte}); }); + } + + private: + boost::sml::sm<detail::transition_table> m_state_machine; + }; + +} // namespace ttwhy |
