ladybird/Userland/Libraries/LibVT/EscapeSequenceParser.h
Daniel Bertalan be519022c3 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.
2021-05-16 11:50:56 +02:00

78 lines
2.3 KiB
C++

/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Debug.h>
#include <AK/Platform.h>
#include <AK/Span.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <LibVT/EscapeSequenceStateMachine.h>
namespace VT {
class EscapeSequenceExecutor {
public:
virtual ~EscapeSequenceExecutor() { }
using Parameters = Span<const unsigned>;
using Intermediates = Span<const u8>;
using OscParameter = Span<const u8>;
using OscParameters = Span<const OscParameter>;
virtual void emit_code_point(u32) = 0;
virtual void execute_control_code(u8) = 0;
virtual void execute_escape_sequence(Intermediates intermediates, bool ignore, u8 last_byte) = 0;
virtual void execute_csi_sequence(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte) = 0;
virtual void execute_osc_sequence(OscParameters parameters, u8 last_byte) = 0;
virtual void dcs_hook(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte) = 0;
virtual void receive_dcs_char(u8 byte) = 0;
virtual void execute_dcs_sequence() = 0;
};
class EscapeSequenceParser {
public:
explicit EscapeSequenceParser(EscapeSequenceExecutor&);
~EscapeSequenceParser();
ALWAYS_INLINE void on_input(u8 byte)
{
dbgln_if(ESCAPE_SEQUENCE_DEBUG, "on_input {:02x}", byte);
m_state_machine.advance(byte);
}
private:
static constexpr size_t MAX_INTERMEDIATES = 2;
static constexpr size_t MAX_PARAMETERS = 16;
static constexpr size_t MAX_OSC_PARAMETERS = 16;
using Intermediates = EscapeSequenceExecutor::Intermediates;
using OscParameter = EscapeSequenceExecutor::OscParameter;
void perform_action(EscapeSequenceStateMachine::Action, u8);
EscapeSequenceExecutor& m_executor;
EscapeSequenceStateMachine m_state_machine;
u32 m_code_point { 0 };
u8 m_intermediates[MAX_INTERMEDIATES];
u8 m_intermediate_idx { 0 };
Intermediates intermediates() const { return { m_intermediates, m_intermediate_idx }; }
Vector<OscParameter> osc_parameters() const;
Vector<unsigned, 4> m_param_vector;
unsigned m_param { 0 };
Vector<u8> m_osc_parameter_indexes;
Vector<u8, 16> m_osc_raw;
bool m_ignoring { false };
};
}