Fixes.
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
#include "bed.h"
|
||||
#include "internal/parser/parser.h"
|
||||
|
||||
namespace bed::internal::parser {
|
||||
void Parser::locator(AddressPromise &addr) {
|
||||
addr.base = AddressPromise::None{};
|
||||
switch (peek()) {
|
||||
case '.':
|
||||
advance();
|
||||
addr.base = AddressPromise::Current{};
|
||||
break;
|
||||
case '$':
|
||||
advance();
|
||||
addr.base = AddressPromise::Last{};
|
||||
break;
|
||||
case '%':
|
||||
advance();
|
||||
addr.base = AddressPromise::LastRange{};
|
||||
break;
|
||||
case '[':
|
||||
advance();
|
||||
addr.base = AddressPromise::Block{Direction::Backward};
|
||||
break;
|
||||
case ']':
|
||||
advance();
|
||||
addr.base = AddressPromise::Block{Direction::Forward};
|
||||
break;
|
||||
case '^':
|
||||
advance();
|
||||
addr.base = AddressPromise::Diagnostic{Direction::Backward};
|
||||
break;
|
||||
case '~':
|
||||
advance();
|
||||
addr.base = AddressPromise::Diagnostic{Direction::Forward};
|
||||
break;
|
||||
case '\'':
|
||||
advance();
|
||||
if (('a' <= peek() && peek() <= 'z')
|
||||
|| ('A' <= peek() && peek() <= 'Z'))
|
||||
addr.base = AddressPromise::Mark{peek()};
|
||||
else
|
||||
throw ed_error("Valid mark needed after \'");
|
||||
advance();
|
||||
break;
|
||||
case '{': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
std::string func;
|
||||
std::string arg;
|
||||
while (peek(j) != '}') {
|
||||
if (peek(j) == '\0')
|
||||
throw ed_error("Scripted address not terminated");
|
||||
if (peek(j) == '\\')
|
||||
++j;
|
||||
if (peek(j) == ':') {
|
||||
func = peek_str(j);
|
||||
advance(j + 1);
|
||||
j = 0;
|
||||
continue;
|
||||
}
|
||||
++j;
|
||||
}
|
||||
if (func.size())
|
||||
arg = peek_str(j);
|
||||
else
|
||||
func = peek_str(j);
|
||||
addr.base = AddressPromise::Scripted{std::move(func), std::move(arg)};
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '/': {
|
||||
advance();
|
||||
uint64_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == '/')
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
addr.base = AddressPromise::Regex(
|
||||
Direction::Forward,
|
||||
std::string(peek_str(j))
|
||||
);
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '?': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == '?')
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
addr.base = AddressPromise::Regex(
|
||||
Direction::Backward,
|
||||
std::string(peek_str(j))
|
||||
);
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '<': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '>'
|
||||
&& peek(j) != '<'
|
||||
&& peek(j) != '\0')
|
||||
j++;
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
case '>':
|
||||
addr.base = AddressPromise::SymbolDefinition{
|
||||
std::string(peek_str(j))
|
||||
};
|
||||
break;
|
||||
case '<':
|
||||
addr.base = AddressPromise::SymbolReference{
|
||||
Direction::Backward,
|
||||
std::string(peek_str(j))
|
||||
};
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '>': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '>' && peek(j) != '\0')
|
||||
j++;
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
throw ed_error("Unterminated symbol reference addressing.");
|
||||
case '>':
|
||||
addr.base = AddressPromise::SymbolReference{
|
||||
Direction::Forward,
|
||||
std::string(peek_str(j))
|
||||
};
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '+': {
|
||||
advance();
|
||||
addr.base = AddressPromise::Current{};
|
||||
uint16_t j = 0;
|
||||
uint64_t num = 0;
|
||||
while ('0' <= peek(j) && peek(j) <= '9') {
|
||||
num = num * 10 + (peek(j) - '0');
|
||||
j++;
|
||||
}
|
||||
if (j == 0)
|
||||
num = 1;
|
||||
advance(j);
|
||||
addr.offset += num;
|
||||
} break;
|
||||
case '-': {
|
||||
advance();
|
||||
addr.base = AddressPromise::Current{};
|
||||
uint16_t j = 0;
|
||||
uint64_t num = 0;
|
||||
while ('0' <= peek(j) && peek(j) <= '9') {
|
||||
num = num * 10 + (peek(j) - '0');
|
||||
j++;
|
||||
}
|
||||
if (j == 0)
|
||||
num = 1;
|
||||
advance(j);
|
||||
addr.offset -= num;
|
||||
} break;
|
||||
default:
|
||||
if ('0' <= peek() && peek() <= '9') {
|
||||
uint64_t num = 0;
|
||||
while ('0' <= peek() && peek() <= '9') {
|
||||
num = num * 10 + (peek() - '0');
|
||||
advance();
|
||||
}
|
||||
addr.base = AddressPromise::Number{num};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t Parser::offset() {
|
||||
int64_t offset = 0;
|
||||
while (peek() == '+' || peek() == '-'
|
||||
|| ('0' <= peek() && peek() <= '9')) {
|
||||
bool positive = peek() != '-';
|
||||
if (peek() == '+' || peek() == '-')
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
int64_t num = 0;
|
||||
while ('0' <= peek(j) && peek(j) <= '9')
|
||||
num = num * 10 + (peek(j++) - '0');
|
||||
if (j == 0)
|
||||
num = 1;
|
||||
advance(j);
|
||||
offset += positive ? num : -num;
|
||||
skip_ws();
|
||||
}
|
||||
skip_ws();
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Parser::address(AddressPromise &addr) {
|
||||
if (peek() == ':') {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != ':' && peek(j) != '\0')
|
||||
j++;
|
||||
addr.bufname = peek_str(j);
|
||||
advance(j);
|
||||
if (peek() == ':')
|
||||
advance();
|
||||
}
|
||||
skip_ws();
|
||||
if (peek() == '\0')
|
||||
return;
|
||||
locator(addr);
|
||||
skip_ws();
|
||||
addr.offset += offset();
|
||||
}
|
||||
|
||||
void Parser::addresses(std::vector<AddressPromise> &addresses) {
|
||||
skip_ws();
|
||||
addresses.push_back({});
|
||||
auto *addr = &addresses.back();
|
||||
address(*addr);
|
||||
while (peek() == ',' || peek() == ';') {
|
||||
addr->jumping = peek() == ';';
|
||||
advance();
|
||||
skip_ws();
|
||||
addresses.push_back({});
|
||||
addr = &addresses.back();
|
||||
address(*addr);
|
||||
}
|
||||
if (addresses.size() == 1
|
||||
&& addr->offset == 0 && !addr->bufname.has_value()
|
||||
&& std::holds_alternative<AddressPromise::None>(addr->base))
|
||||
addresses.pop_back();
|
||||
}
|
||||
} // namespace bed::internal::parser
|
||||
@@ -6,13 +6,16 @@ namespace bed::internal::parser {
|
||||
buffer::Line AddressPromise::resolve(BEd &ctx) {
|
||||
buffer::Line result = std::visit(
|
||||
[&](auto const &addr) -> buffer::Line {
|
||||
if (!bufname.has_value())
|
||||
if (!bufname.has_value() || bufname->empty())
|
||||
throw ed_error("Buffer name can't be empty.");
|
||||
buffer::Line line = {*bufname, 0};
|
||||
auto &buf = ctx.buffer(line.buffername);
|
||||
using T = std::decay_t<decltype(addr)>;
|
||||
if constexpr (std::is_same_v<T, None>) {
|
||||
throw ed_error("no address");
|
||||
if (line.buffername == ctx.current().buffername)
|
||||
line.number = ctx.current().number;
|
||||
else
|
||||
line.number = buf.lines();
|
||||
} else if constexpr (std::is_same_v<T, Current>) {
|
||||
if (line.buffername == ctx.current().buffername)
|
||||
line.number = ctx.current().number;
|
||||
@@ -133,15 +136,18 @@ std::optional<buffer::Line> AddressPromise::get_line(BEd &ctx, std::vector<Addre
|
||||
prev_set = true;
|
||||
} else {
|
||||
if (std::holds_alternative<None>(curr.base)) {
|
||||
if (!prev_set)
|
||||
return std::nullopt;
|
||||
if (prev_given) {
|
||||
curr.base = prev.base;
|
||||
curr.offset = prev.offset;
|
||||
} else {
|
||||
curr.base = Last();
|
||||
curr.offset = 0;
|
||||
if (prev_set) {
|
||||
if (prev_given) {
|
||||
curr.base = prev.base;
|
||||
curr.offset = prev.offset;
|
||||
} else {
|
||||
curr.base = Last();
|
||||
curr.offset = 0;
|
||||
}
|
||||
}
|
||||
} else if (std::holds_alternative<LastRange>(curr.base)) {
|
||||
curr.bufname = ctx.prev.buffername;
|
||||
curr.base = Number(ctx.prev.end);
|
||||
}
|
||||
return curr.resolve(ctx);
|
||||
}
|
||||
@@ -193,16 +199,18 @@ std::optional<buffer::Range> AddressPromise::get_range(BEd &ctx, std::vector<Add
|
||||
prev_set = true;
|
||||
} else {
|
||||
if (std::holds_alternative<None>(curr.base)) {
|
||||
if (!prev_set)
|
||||
return std::nullopt;
|
||||
if (prev_given) {
|
||||
curr.base = prev.base;
|
||||
curr.offset = prev.offset;
|
||||
} else {
|
||||
curr.base = Last();
|
||||
curr.offset = 0;
|
||||
if (prev_set) {
|
||||
if (prev_given) {
|
||||
curr.base = prev.base;
|
||||
curr.offset = prev.offset;
|
||||
} else {
|
||||
curr.base = Last();
|
||||
curr.offset = 0;
|
||||
}
|
||||
return buffer::Range(prev.resolve(ctx), curr.resolve(ctx));
|
||||
}
|
||||
return buffer::Range(prev.resolve(ctx), curr.resolve(ctx));
|
||||
} else if (std::holds_alternative<LastRange>(curr.base)) {
|
||||
return ctx.prev;
|
||||
}
|
||||
if (prev_given)
|
||||
return buffer::Range(prev.resolve(ctx), curr.resolve(ctx));
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "bed.h"
|
||||
#include "internal/parser/parser.h"
|
||||
|
||||
namespace bed::internal::parser {
|
||||
void Parser::operation() {
|
||||
if (peek() == '\0') {
|
||||
command->function = &bed.no_op;
|
||||
return;
|
||||
}
|
||||
uint64_t len = bed.functions.longest_match(peek_str());
|
||||
if (len == 0)
|
||||
throw ed_error("Function not found.");
|
||||
functions::Function *function = bed.functions.get_ptr(peek_str(len));
|
||||
advance(len);
|
||||
command->function = function;
|
||||
char suffix = '\0';
|
||||
switch (command->function->argument_kind) {
|
||||
case functions::Function::ArgumentKind::None:
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Number:
|
||||
skip_ws();
|
||||
command->argument = offset();
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Mark:
|
||||
if (('a' <= peek() && peek() <= 'z')
|
||||
|| ('A' <= peek() && peek() <= 'Z'))
|
||||
command->argument = peek();
|
||||
else
|
||||
throw ed_error("Valid mark needed.");
|
||||
advance();
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Any:
|
||||
command->argument = std::string(peek_str());
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Global: {
|
||||
char delim;
|
||||
std::string val;
|
||||
switch (peek()) {
|
||||
case '\0':
|
||||
throw ed_error("Command needs a delimited value.");
|
||||
case '{': {
|
||||
advance();
|
||||
delim = '}';
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '}' && peek(j) != '\0') {
|
||||
if (peek(j) == '\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
throw ed_error("Unterminated {");
|
||||
case '}':
|
||||
val = peek_str(j);
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '<': {
|
||||
advance();
|
||||
delim = '<';
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '>' || peek(j) != '\0') {
|
||||
if (peek(j) == '\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
throw ed_error("Unterminated <");
|
||||
case '>':
|
||||
val = peek_str(j);
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '^':
|
||||
case '~':
|
||||
advance();
|
||||
delim = '^';
|
||||
break;
|
||||
default:
|
||||
delim = peek();
|
||||
advance();
|
||||
uint64_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == delim)
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
val = peek_str(j);
|
||||
advance(j + 1);
|
||||
}
|
||||
command->argument = functions::Function::GlobalArg(delim, std::move(val));
|
||||
} break;
|
||||
case functions::Function::ArgumentKind::File:
|
||||
skip_ws();
|
||||
switch (peek()) {
|
||||
case '!':
|
||||
advance();
|
||||
command->argument = functions::Function::ShellArg(std::string(peek_str()));
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
case '\0':
|
||||
command->argument = std::monostate();
|
||||
break;
|
||||
default:
|
||||
command->argument = std::filesystem::path(peek_str());
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Line:
|
||||
command->argument = buffer::Line();
|
||||
addresses(command->argument_addresses);
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Range:
|
||||
command->argument = buffer::Range();
|
||||
addresses(command->argument_addresses);
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Regex: {
|
||||
char delim = peek();
|
||||
if (delim == '\0')
|
||||
throw ed_error("regex expected");
|
||||
advance();
|
||||
uint64_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
throw ed_error("Unterminated regex");
|
||||
if (peek(j) == delim)
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
std::string expression(peek_str(j));
|
||||
advance(j + 1);
|
||||
j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == delim)
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
std::string replacement;
|
||||
if (peek(j) != '\0') {
|
||||
replacement = peek_str(j);
|
||||
advance(j + 1);
|
||||
}
|
||||
std::string options;
|
||||
if (peek() != '\0') {
|
||||
options = std::string(peek_str());
|
||||
advance(peek_str().size());
|
||||
}
|
||||
std::erase_if(options, [&](char c) {
|
||||
if (bed.suffixes[c - 'a'].has_value()) {
|
||||
suffix = c;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
command->argument = functions::Function::RegexArg(expression, replacement, options);
|
||||
} break;
|
||||
case functions::Function::ArgumentKind::Ruby:
|
||||
command->argument = functions::Function::RubyArg(std::string(peek_str()));
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Shell:
|
||||
command->argument = functions::Function::ShellArg(std::string(peek_str()));
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
}
|
||||
if (!suffix) {
|
||||
suffix = peek();
|
||||
advance();
|
||||
}
|
||||
if (suffix) {
|
||||
auto &s = bed.suffixes[suffix - 'a'];
|
||||
if (s.has_value())
|
||||
command->suffix = &s.value();
|
||||
else
|
||||
throw ed_error("Invalid suffix.");
|
||||
}
|
||||
}
|
||||
} // namespace bed::internal::parser
|
||||
@@ -19,455 +19,6 @@ void Parser::skip_ws() {
|
||||
advance();
|
||||
}
|
||||
|
||||
void Parser::locator(AddressPromise &addr) {
|
||||
addr.base = AddressPromise::None{};
|
||||
switch (peek()) {
|
||||
case '.':
|
||||
advance();
|
||||
addr.base = AddressPromise::Current{};
|
||||
break;
|
||||
case '$':
|
||||
advance();
|
||||
addr.base = AddressPromise::Last{};
|
||||
break;
|
||||
case '%':
|
||||
advance();
|
||||
addr.base = AddressPromise::LastRange{};
|
||||
break;
|
||||
case '[':
|
||||
advance();
|
||||
addr.base = AddressPromise::Block{Direction::Backward};
|
||||
break;
|
||||
case ']':
|
||||
advance();
|
||||
addr.base = AddressPromise::Block{Direction::Forward};
|
||||
break;
|
||||
case '^':
|
||||
advance();
|
||||
addr.base = AddressPromise::Diagnostic{Direction::Backward};
|
||||
break;
|
||||
case '~':
|
||||
advance();
|
||||
addr.base = AddressPromise::Diagnostic{Direction::Forward};
|
||||
break;
|
||||
case '\'':
|
||||
advance();
|
||||
if (('a' <= peek() && peek() <= 'z')
|
||||
|| ('A' <= peek() && peek() <= 'Z'))
|
||||
addr.base = AddressPromise::Mark{peek()};
|
||||
else
|
||||
throw ed_error("Valid mark needed after \'");
|
||||
advance();
|
||||
break;
|
||||
case '{': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
std::string func;
|
||||
std::string arg;
|
||||
while (peek(j) != '}') {
|
||||
if (peek(j) == '\0')
|
||||
throw ed_error("Scripted address not terminated");
|
||||
if (peek(j) == '\\')
|
||||
++j;
|
||||
if (peek(j) == ':') {
|
||||
func = peek_str(j);
|
||||
advance(j + 1);
|
||||
j = 0;
|
||||
continue;
|
||||
}
|
||||
++j;
|
||||
}
|
||||
if (func.size())
|
||||
arg = peek_str(j);
|
||||
else
|
||||
func = peek_str(j);
|
||||
addr.base = AddressPromise::Scripted{std::move(func), std::move(arg)};
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '/': {
|
||||
advance();
|
||||
uint64_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == '/')
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
addr.base = AddressPromise::Regex(
|
||||
Direction::Forward,
|
||||
std::string(peek_str(j))
|
||||
);
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '?': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == '?')
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
addr.base = AddressPromise::Regex(
|
||||
Direction::Backward,
|
||||
std::string(peek_str(j))
|
||||
);
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '<': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '>'
|
||||
&& peek(j) != '<'
|
||||
&& peek(j) != '\0')
|
||||
j++;
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
case '>':
|
||||
addr.base = AddressPromise::SymbolDefinition{
|
||||
std::string(peek_str(j))
|
||||
};
|
||||
break;
|
||||
case '<':
|
||||
addr.base = AddressPromise::SymbolReference{
|
||||
Direction::Backward,
|
||||
std::string(peek_str(j))
|
||||
};
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '>': {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '>' && peek(j) != '\0')
|
||||
j++;
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
throw ed_error("Unterminated symbol reference addressing.");
|
||||
case '>':
|
||||
addr.base = AddressPromise::SymbolReference{
|
||||
Direction::Forward,
|
||||
std::string(peek_str(j))
|
||||
};
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '+': {
|
||||
advance();
|
||||
addr.base = AddressPromise::Current{};
|
||||
uint16_t j = 0;
|
||||
uint64_t num = 0;
|
||||
while ('0' <= peek(j) && peek(j) <= '9') {
|
||||
num = num * 10 + (peek(j) - '0');
|
||||
j++;
|
||||
}
|
||||
if (j == 0)
|
||||
num = 1;
|
||||
advance(j);
|
||||
addr.offset += num;
|
||||
} break;
|
||||
case '-': {
|
||||
advance();
|
||||
addr.base = AddressPromise::Current{};
|
||||
uint16_t j = 0;
|
||||
uint64_t num = 0;
|
||||
while ('0' <= peek(j) && peek(j) <= '9') {
|
||||
num = num * 10 + (peek(j) - '0');
|
||||
j++;
|
||||
}
|
||||
if (j == 0)
|
||||
num = 1;
|
||||
advance(j);
|
||||
addr.offset -= num;
|
||||
} break;
|
||||
default:
|
||||
if ('0' <= peek() && peek() <= '9') {
|
||||
uint64_t num = 0;
|
||||
while ('0' <= peek() && peek() <= '9') {
|
||||
num = num * 10 + (peek() - '0');
|
||||
advance();
|
||||
}
|
||||
addr.base = AddressPromise::Number{num};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t Parser::offset() {
|
||||
int64_t offset = 0;
|
||||
while (peek() == '+' || peek() == '-'
|
||||
|| ('0' <= peek() && peek() <= '9')) {
|
||||
bool positive = peek() != '-';
|
||||
if (peek() == '+' || peek() == '-')
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
int64_t num = 0;
|
||||
while ('0' <= peek(j) && peek(j) <= '9')
|
||||
num = num * 10 + (peek(j++) - '0');
|
||||
if (j == 0)
|
||||
num = 1;
|
||||
advance(j);
|
||||
offset += positive ? num : -num;
|
||||
skip_ws();
|
||||
}
|
||||
skip_ws();
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Parser::address(AddressPromise &addr) {
|
||||
if (peek() == ':') {
|
||||
advance();
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != ':' && peek(j) != '\0')
|
||||
j++;
|
||||
addr.bufname = peek_str(j);
|
||||
advance(j);
|
||||
if (peek() == ':')
|
||||
advance();
|
||||
}
|
||||
skip_ws();
|
||||
if (peek() == '\0')
|
||||
return;
|
||||
locator(addr);
|
||||
skip_ws();
|
||||
addr.offset += offset();
|
||||
}
|
||||
|
||||
void Parser::addresses(std::vector<AddressPromise> &addresses) {
|
||||
skip_ws();
|
||||
addresses.push_back({});
|
||||
auto *addr = &addresses.back();
|
||||
address(*addr);
|
||||
while (peek() == ',' || peek() == ';') {
|
||||
addr->jumping = peek() == ';';
|
||||
advance();
|
||||
skip_ws();
|
||||
addresses.push_back({});
|
||||
addr = &addresses.back();
|
||||
address(*addr);
|
||||
}
|
||||
if (addresses.size() == 1
|
||||
&& addr->offset == 0 && addr->bufname.has_value()
|
||||
&& std::holds_alternative<AddressPromise::None>(addr->base))
|
||||
addresses.pop_back();
|
||||
}
|
||||
|
||||
void Parser::operation() {
|
||||
if (peek() == '\0') {
|
||||
command->function = &bed.no_op;
|
||||
return;
|
||||
}
|
||||
uint64_t len = bed.functions.longest_match(peek_str());
|
||||
if (len == 0)
|
||||
throw ed_error("Function not found.");
|
||||
functions::Function *function = bed.functions.get_ptr(peek_str(len));
|
||||
advance(len);
|
||||
command->function = function;
|
||||
char suffix = '\0';
|
||||
switch (command->function->argument_kind) {
|
||||
case functions::Function::ArgumentKind::None:
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Number:
|
||||
skip_ws();
|
||||
command->argument = offset();
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Mark:
|
||||
if (('a' <= peek() && peek() <= 'z')
|
||||
|| ('A' <= peek() && peek() <= 'Z'))
|
||||
command->argument = peek();
|
||||
else
|
||||
throw ed_error("Valid mark needed.");
|
||||
advance();
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Any:
|
||||
command->argument = std::string(peek_str());
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Global: {
|
||||
char delim;
|
||||
std::string val;
|
||||
switch (peek()) {
|
||||
case '\0':
|
||||
throw ed_error("Command needs a delimited value.");
|
||||
case '{': {
|
||||
advance();
|
||||
delim = '}';
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '}' && peek(j) != '\0') {
|
||||
if (peek(j) == '\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
throw ed_error("Unterminated {");
|
||||
case '}':
|
||||
val = peek_str(j);
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '<': {
|
||||
advance();
|
||||
delim = '<';
|
||||
uint16_t j = 0;
|
||||
while (peek(j) != '>' || peek(j) != '\0') {
|
||||
if (peek(j) == '\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
switch (peek(j)) {
|
||||
case '\0':
|
||||
throw ed_error("Unterminated <");
|
||||
case '>':
|
||||
val = peek_str(j);
|
||||
break;
|
||||
}
|
||||
advance(j + 1);
|
||||
} break;
|
||||
case '^':
|
||||
case '~':
|
||||
advance();
|
||||
delim = '^';
|
||||
break;
|
||||
default:
|
||||
delim = peek();
|
||||
advance();
|
||||
uint64_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == delim)
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
val = peek_str(j);
|
||||
advance(j + 1);
|
||||
}
|
||||
command->argument = functions::Function::GlobalArg(delim, std::move(val));
|
||||
} break;
|
||||
case functions::Function::ArgumentKind::File:
|
||||
skip_ws();
|
||||
switch (peek()) {
|
||||
case '!':
|
||||
advance();
|
||||
command->argument = functions::Function::ShellArg(std::string(peek_str()));
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
case '\0':
|
||||
command->argument = std::monostate();
|
||||
break;
|
||||
default:
|
||||
command->argument = std::filesystem::path(peek_str());
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Line:
|
||||
command->argument = buffer::Line();
|
||||
addresses(command->argument_addresses);
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Range:
|
||||
command->argument = buffer::Range();
|
||||
addresses(command->argument_addresses);
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Regex: {
|
||||
char delim = peek();
|
||||
if (delim == '\0')
|
||||
throw ed_error("regex expected");
|
||||
advance();
|
||||
uint64_t j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
throw ed_error("Unterminated regex");
|
||||
if (peek(j) == delim)
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
std::string expression(peek_str(j));
|
||||
advance(j + 1);
|
||||
j = 0;
|
||||
while (true) {
|
||||
if (peek(j) == '\0')
|
||||
break;
|
||||
if (peek(j) == delim)
|
||||
break;
|
||||
else if (peek(j) == '\\')
|
||||
j += 2;
|
||||
else if (peek(j) == '[')
|
||||
while (peek(j) != '\0' && cmd[i] != ']')
|
||||
j++;
|
||||
else
|
||||
j++;
|
||||
}
|
||||
std::string replacement;
|
||||
if (peek(j) != '\0') {
|
||||
replacement = peek_str(j);
|
||||
advance(j + 1);
|
||||
}
|
||||
std::string options;
|
||||
if (peek() != '\0') {
|
||||
options = std::string(peek_str());
|
||||
advance(peek_str().size());
|
||||
}
|
||||
std::erase_if(options, [&](char c) {
|
||||
if (bed.suffixes[c - 'a'].has_value()) {
|
||||
suffix = c;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
command->argument = functions::Function::RegexArg(expression, replacement, options);
|
||||
} break;
|
||||
case functions::Function::ArgumentKind::Ruby:
|
||||
command->argument = functions::Function::RubyArg(std::string(peek_str()));
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
case functions::Function::ArgumentKind::Shell:
|
||||
command->argument = functions::Function::ShellArg(std::string(peek_str()));
|
||||
advance(peek_str().size());
|
||||
break;
|
||||
}
|
||||
if (!suffix) {
|
||||
suffix = peek();
|
||||
advance();
|
||||
}
|
||||
if (suffix) {
|
||||
auto &s = bed.suffixes[suffix - 'a'];
|
||||
if (s.has_value())
|
||||
command->suffix = &s.value();
|
||||
else
|
||||
throw ed_error("Invalid suffix.");
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parse() {
|
||||
skip_ws();
|
||||
if (peek() == '@') {
|
||||
@@ -486,14 +37,14 @@ void Parser::parse() {
|
||||
|
||||
Parser::Parser(
|
||||
std::string_view cmd, BEd &bed, Command *command,
|
||||
std::vector<io::Token> *tokens, CompletionContext *completion
|
||||
std::vector<ui::Token> *tokens, CompletionContext *completion
|
||||
) : bed(bed), cmd(cmd), command(command), tokens(tokens), completion(completion) {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
Command Parser::get_command(std::string_view cmd, BEd &bed) {
|
||||
Command c;
|
||||
std::vector<io::Token> tokens;
|
||||
std::vector<ui::Token> tokens;
|
||||
CompletionContext completion;
|
||||
Parser p(cmd, bed, &c, &tokens, &completion);
|
||||
p.parse();
|
||||
@@ -502,7 +53,7 @@ Command Parser::get_command(std::string_view cmd, BEd &bed) {
|
||||
|
||||
std::vector<AddressPromise> Parser::get_addresses(std::string_view cmd, BEd &bed) {
|
||||
std::vector<AddressPromise> result;
|
||||
std::vector<io::Token> tokens;
|
||||
std::vector<ui::Token> tokens;
|
||||
CompletionContext completion;
|
||||
Parser p(cmd, bed, nullptr, &tokens, &completion);
|
||||
p.addresses(result);
|
||||
|
||||
Reference in New Issue
Block a user