diff options
author | Daniel Bertalan <dani@danielbertalan.dev> | 2021-05-08 20:37:43 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-16 11:50:56 +0200 |
commit | be519022c3326f7d5cec964b6d3b72d419b0f522 (patch) | |
tree | e8d4698840276142443a0d0b1c99022bc6ccb2cf /Userland/DevTools | |
parent | 1b347298f118e4e78e7cef41096623913c9e640e (diff) | |
download | serenity-be519022c3326f7d5cec964b6d3b72d419b0f522.zip |
LibVT: Implement new ANSI escape sequence parser
This commit replaces the former, hand-written parser with a new one that
can be generated automatically according to a state change diagram.
The new `EscapeSequenceParser` class provides a more ergonomic interface
to dealing with escape sequences. This interface has been inspired by
Alacritty's [vte library](https://github.com/alacritty/vte/).
I tried to avoid changing the application logic inside the `Terminal`
class. While this code has not been thoroughly tested, I can't find
regressions in the basic command line utilities or `vttest`.
`Terminal` now displays nicer debug messages when it encounters an
unknown escape sequence. Defensive programming and bounds checks have
been added where we access parameters, and as a result, we can now
endure 4-5 seconds of `cat /dev/urandom`. :D
We generate EscapeSequenceStateMachine.h when building the in-kernel
LibVT, and we assume that the file is already in place when the userland
library is being built. This will probably cause problems later on, but
I can't find a way to do it nicely.
Diffstat (limited to 'Userland/DevTools')
-rw-r--r-- | Userland/DevTools/StateMachineGenerator/main.cpp | 189 |
1 files changed, 73 insertions, 116 deletions
diff --git a/Userland/DevTools/StateMachineGenerator/main.cpp b/Userland/DevTools/StateMachineGenerator/main.cpp index 46b4e02d7c..d2f5f02892 100644 --- a/Userland/DevTools/StateMachineGenerator/main.cpp +++ b/Userland/DevTools/StateMachineGenerator/main.cpp @@ -211,18 +211,15 @@ parse_state_machine(StringView input) } void output_header(const StateMachine&, SourceGenerator&); -void output_cpp(const StateMachine&, SourceGenerator&); int main(int argc, char** argv) { Core::ArgsParser args_parser; const char* path = nullptr; - bool header_mode = false; - args_parser.add_option(header_mode, "Generate .h file", "header", 'H'); args_parser.add_positional_argument(path, "Path to parser description", "input", Core::ArgsParser::Required::Yes); args_parser.parse(argc, argv); - auto file_or_error = Core::File::open(path, Core::IODevice::ReadOnly); + auto file_or_error = Core::File::open(path, Core::OpenMode::ReadOnly); if (file_or_error.is_error()) { fprintf(stderr, "Cannot open %s\n", path); } @@ -232,10 +229,7 @@ int main(int argc, char** argv) StringBuilder builder; SourceGenerator generator { builder }; - if (header_mode) - output_header(*state_machine, generator); - else - output_cpp(*state_machine, generator); + output_header(*state_machine, generator); outln("{}", generator.as_string_view()); return 0; } @@ -340,9 +334,66 @@ public: typedef Function<void(Action, u8)> Handler; - @class_name@(Handler); + @class_name@(Handler handler) + : m_handler(move(handler)) + { + } + + void advance(u8 byte) + { + auto next_state = lookup_state_transition(byte); + bool state_will_change = next_state.new_state != m_state && next_state.new_state != State::_Anywhere; + + // only run exit directive if state is being changed + if (state_will_change) { + switch (m_state) { +)~~~"); + for (auto s : machine.states) { + auto state_generator = generator.fork(); + if (s.exit_action.has_value()) { + state_generator.set("state_name", s.name); + state_generator.set("action", s.exit_action.value()); + state_generator.append(R"~~~( + case State::@state_name@: + m_handler(Action::@action@, byte); + break; +)~~~"); + } + } + generator.append(R"~~~( + default: + break; + } + } - void advance(u8); + if (next_state.action != Action::_Ignore) + m_handler(next_state.action, byte); + m_state = next_state.new_state; + + // only run entry directive if state is being changed + if (state_will_change) + { + switch (next_state.new_state) + { +)~~~"); + for (auto state : machine.states) { + auto state_generator = generator.fork(); + if (state.entry_action.has_value()) { + state_generator.set("state_name", state.name); + state_generator.set("action", state.entry_action.value()); + state_generator.append(R"~~~( + case State::@state_name@: + m_handler(Action::@action@, byte); + break; +)~~~"); + } + } + generator.append(R"~~~( + default: + break; + } + } + } private: enum class State : u8 { @@ -370,121 +421,27 @@ private: Handler m_handler; - StateTransition lookup_state_transition(u8); -)~~~"); - - auto table_generator = generator.fork(); - generate_lookup_table(machine, table_generator); - generator.append(R"~~~( -}; // end @class_name@ -)~~~"); - - if (machine.namespaces.has_value()) { - generator.append(R"~~~( -} // end namespace -)~~~"); - } -} - -void output_cpp(const StateMachine& machine, SourceGenerator& generator) -{ - VERIFY(!machine.name.is_empty()); - generator.set("class_name", machine.name); - generator.set("state_count", String::number(machine.states.size() + 1)); - - generator.append(R"~~~( -#include "@class_name@.h" -#include <AK/Function.h> -#include <AK/Types.h> -)~~~"); - if (machine.namespaces.has_value()) { - generator.set("namespace", machine.namespaces.value()); - generator.append(R"~~~( -namespace @namespace@ { + ALWAYS_INLINE StateTransition lookup_state_transition(u8 byte) + { + VERIFY((u8)m_state < @state_count@); )~~~"); - } - generator.append(R"~~~( -@class_name@::@class_name@(Handler handler) - : m_handler(move(handler)) -{ -} - -ALWAYS_INLINE @class_name@::StateTransition @class_name@::lookup_state_transition(u8 byte) -{ - VERIFY((u8)m_state < @state_count@); - )~~~"); if (machine.anywhere.has_value()) { generator.append(R"~~~( - auto anywhere_state = STATE_TRANSITION_TABLE[0][byte]; - if (anywhere_state.new_state != @class_name@::State::_Anywhere || anywhere_state.action != @class_name@::Action::_Ignore) - return anywhere_state; - else -)~~~"); - } - generator.append(R"~~~( - return STATE_TRANSITION_TABLE[(u8)m_state][byte]; -} -)~~~"); - - generator.append(R"~~~( - -void @class_name@::advance(u8 byte) -{ - auto next_state = lookup_state_transition(byte); - bool state_will_change = next_state.new_state != m_state && next_state.new_state != @class_name@::State::_Anywhere; - - // only run exit directive if state is being changed - if (state_will_change) - { - switch (m_state) - { -)~~~"); - for (auto s : machine.states) { - auto state_generator = generator.fork(); - if (s.exit_action.has_value()) { - state_generator.set("state_name", s.name); - state_generator.set("action", s.exit_action.value()); - state_generator.append(R"~~~( - case @class_name@::State::@state_name@: - m_handler(Action::@action@, byte); - break; + auto anywhere_state = STATE_TRANSITION_TABLE[0][byte]; + if (anywhere_state.new_state != State::_Anywhere || anywhere_state.action != Action::_Ignore) + return anywhere_state; + else )~~~"); - } } generator.append(R"~~~( - default: - break; - } + return STATE_TRANSITION_TABLE[(u8)m_state][byte]; } - - if (next_state.action != @class_name@::Action::_Ignore) - m_handler(next_state.action, byte); - m_state = next_state.new_state; - - // only run entry directive if state is being changed - if (state_will_change) - { - switch (next_state.new_state) - { )~~~"); - for (auto state : machine.states) { - auto state_generator = generator.fork(); - if (state.entry_action.has_value()) { - state_generator.set("state_name", state.name); - state_generator.set("action", state.entry_action.value()); - state_generator.append(R"~~~( - case @class_name@::State::@state_name@: - m_handler(Action::@action@, byte); - break; -)~~~"); - } - } + + auto table_generator = generator.fork(); + generate_lookup_table(machine, table_generator); generator.append(R"~~~( - default: - break; - } - } -} +}; // end @class_name@ )~~~"); if (machine.namespaces.has_value()) { |