aboutsummaryrefslogtreecommitdiff
path: root/ttwhy/scanner.cppm
diff options
context:
space:
mode:
Diffstat (limited to 'ttwhy/scanner.cppm')
-rw-r--r--ttwhy/scanner.cppm127
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