initial commit
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <regex.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sedpp {
|
||||
|
||||
// Address kinds mirror GNU sed's address grammar. Step, Plus, and Modulo are
|
||||
// GNU extensions: FIRST~STEP as a standalone address, and +N/~N only as the
|
||||
// second half of a range.
|
||||
enum class AddressKind {
|
||||
None,
|
||||
Line,
|
||||
Last,
|
||||
Regex,
|
||||
Step,
|
||||
Plus,
|
||||
Modulo,
|
||||
};
|
||||
|
||||
// A parsed address keeps both the original text and numeric forms. The text is
|
||||
// still needed for special cases such as line address 0 and for empty regexes,
|
||||
// which mean "reuse the previous regular expression" at runtime.
|
||||
struct Address {
|
||||
AddressKind kind = AddressKind::None;
|
||||
std::string text;
|
||||
std::uint64_t first = 0;
|
||||
std::uint64_t second = 0;
|
||||
};
|
||||
|
||||
// Parsed state for the s command. flagOrder preserves p/e ordering because GNU
|
||||
// sed changes observable output depending on whether printing happens before or
|
||||
// after shell execution.
|
||||
struct Substitute {
|
||||
std::string pattern;
|
||||
std::string replacement;
|
||||
bool global = false;
|
||||
bool print = false;
|
||||
bool execute = false;
|
||||
bool ignoreCase = false;
|
||||
bool multiline = false;
|
||||
std::uint64_t occurrence = 0;
|
||||
std::string flagOrder;
|
||||
std::string writeFile;
|
||||
};
|
||||
|
||||
// y transliteration operates on decoded character strings, not regex syntax.
|
||||
struct Translate {
|
||||
std::string from;
|
||||
std::string to;
|
||||
};
|
||||
|
||||
// A compiled sed command. The opcode selects which optional payload is valid:
|
||||
// text for a/i/c/r/R/w/W/e, label for :/b/t/T, substitute for s, translate for y.
|
||||
struct Command {
|
||||
std::optional<Address> firstAddress;
|
||||
std::optional<Address> secondAddress;
|
||||
bool negate = false;
|
||||
char opcode = '\0';
|
||||
|
||||
// These fields are intentionally plain. sed commands are compact and varied;
|
||||
// a tiny tagged structure is easier to audit than a deep inheritance tree.
|
||||
std::string text;
|
||||
std::string label;
|
||||
std::optional<Substitute> substitute;
|
||||
std::optional<Translate> translate;
|
||||
int exitCode = 0;
|
||||
int lineLength = 0;
|
||||
|
||||
// Runtime range state. GNU sed keeps range activation per compiled command,
|
||||
// not globally, so the state lives with the command object.
|
||||
bool rangeActive = false;
|
||||
bool rangeJustStarted = false;
|
||||
std::uint64_t rangeCountdown = 0;
|
||||
bool zeroRangeStarted = false;
|
||||
};
|
||||
|
||||
// The compiler flattens the sed script into one vector. Braced blocks remain
|
||||
// explicit { and } opcodes so the runner can skip inactive blocks without a
|
||||
// second tree-shaped representation.
|
||||
struct Program {
|
||||
std::vector<Command> commands;
|
||||
};
|
||||
|
||||
} // namespace sedpp
|
||||
Reference in New Issue
Block a user