module; #include #include #include #include #include export module ttwhy:scanner; import :event; namespace ttwhy::detail { /// Events struct byte_received { char value; }; /// States auto const idle = boost::sml::state; auto const escape_sequence = boost::sml::state; auto const csi_sequence = boost::sml::state; /// Actions auto push_character = [](byte_received const & event, std::vector & queue) { queue.push_back(character_event{event.value}); }; auto push_backspace = [](std::vector & queue) { queue.push_back(control_event{control_key::backspace}); }; auto push_delete = [](std::vector & queue) { queue.push_back(navigation_event{navigation_key::delete_key}); }; auto fallback_escape = [](byte_received const & event, std::vector & 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[is_escape] = escape_sequence, idle + event[is_backspace] / push_backspace = idle, idle + event[is_printable] / push_character = idle, escape_sequence + event[is_csi_introducer] = csi_sequence, escape_sequence + event[!is_csi_introducer] / fallback_escape = idle, csi_sequence + event[is_csi_param] = csi_sequence, csi_sequence + event[is_tilde] / push_delete = idle, csi_sequence + event[is_csi_terminator] = idle ); // clang-format on } }; } // namespace ttwhy::detail export namespace ttwhy { struct ansi_scanner { explicit ansi_scanner(std::vector & queue) : m_state_machine{queue} {} auto process(std::span buffer) -> void { std::ranges::for_each(buffer, [&](auto byte) { m_state_machine.process_event(detail::byte_received{byte}); }); } private: boost::sml::sm m_state_machine; }; } // namespace ttwhy