Only compute command coordinates when necessary
Tracking the line/column of each token takes a surprising big part of the command parsing logic runtime and is only necessary when we hit an error.
This commit is contained in:
parent
49e2ecdcdb
commit
be9b2de0ee
|
@ -101,12 +101,6 @@ Codepoint Reader::peek_next() const
|
||||||
Reader& Reader::operator++()
|
Reader& Reader::operator++()
|
||||||
{
|
{
|
||||||
kak_assert(pos < str.end());
|
kak_assert(pos < str.end());
|
||||||
if (*pos == '\n')
|
|
||||||
{
|
|
||||||
++line;
|
|
||||||
line_start = ++pos;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
utf8::to_next(pos, str.end());
|
utf8::to_next(pos, str.end());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -114,11 +108,7 @@ Reader& Reader::operator++()
|
||||||
Reader& Reader::next_byte()
|
Reader& Reader::next_byte()
|
||||||
{
|
{
|
||||||
kak_assert(pos < str.end());
|
kak_assert(pos < str.end());
|
||||||
if (*pos++ == '\n')
|
++pos;
|
||||||
{
|
|
||||||
++line;
|
|
||||||
line_start = pos;
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,6 +243,22 @@ void skip_blanks_and_comments(Reader& reader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BufferCoord compute_coord(StringView s)
|
||||||
|
{
|
||||||
|
BufferCoord coord{0,0};
|
||||||
|
for (auto c : s)
|
||||||
|
{
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
++coord.line;
|
||||||
|
coord.column = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++coord.column;
|
||||||
|
}
|
||||||
|
return coord;
|
||||||
|
}
|
||||||
|
|
||||||
Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
|
Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
|
||||||
{
|
{
|
||||||
kak_assert(*reader == '%');
|
kak_assert(*reader == '%');
|
||||||
|
@ -278,7 +284,6 @@ Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
|
||||||
};
|
};
|
||||||
|
|
||||||
const Codepoint opening_delimiter = *reader;
|
const Codepoint opening_delimiter = *reader;
|
||||||
auto coord = reader.coord();
|
|
||||||
++reader;
|
++reader;
|
||||||
auto start = reader.pos;
|
auto start = reader.pos;
|
||||||
|
|
||||||
|
@ -291,22 +296,28 @@ Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
|
||||||
const Codepoint closing_delimiter = it->closing;
|
const Codepoint closing_delimiter = it->closing;
|
||||||
auto quoted = parse_quoted_balanced(reader, opening_delimiter, closing_delimiter);
|
auto quoted = parse_quoted_balanced(reader, opening_delimiter, closing_delimiter);
|
||||||
if (throw_on_unterminated and not quoted.terminated)
|
if (throw_on_unterminated and not quoted.terminated)
|
||||||
|
{
|
||||||
|
auto coord = compute_coord({reader.str.begin(), start});
|
||||||
throw parse_error{format("{}:{}: unterminated string '%{}{}...{}'",
|
throw parse_error{format("{}:{}: unterminated string '%{}{}...{}'",
|
||||||
coord.line, coord.column, type_name,
|
coord.line+1, coord.column+1, type_name,
|
||||||
opening_delimiter, closing_delimiter)};
|
opening_delimiter, closing_delimiter)};
|
||||||
|
}
|
||||||
|
|
||||||
return {type, start - str_beg, coord, std::move(quoted.content), quoted.terminated};
|
return {type, start - str_beg, std::move(quoted.content), quoted.terminated};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto quoted = parse_quoted(reader, opening_delimiter);
|
auto quoted = parse_quoted(reader, opening_delimiter);
|
||||||
|
|
||||||
if (throw_on_unterminated and not quoted.terminated)
|
if (throw_on_unterminated and not quoted.terminated)
|
||||||
|
{
|
||||||
|
auto coord = compute_coord({reader.str.begin(), start});
|
||||||
throw parse_error{format("{}:{}: unterminated string '%{}{}...{}'",
|
throw parse_error{format("{}:{}: unterminated string '%{}{}...{}'",
|
||||||
coord.line, coord.column, type_name,
|
coord.line+1, coord.column+1, type_name,
|
||||||
opening_delimiter, opening_delimiter)};
|
opening_delimiter, opening_delimiter)};
|
||||||
|
}
|
||||||
|
|
||||||
return {type, start - str_beg, coord, std::move(quoted.content), quoted.terminated};
|
return {type, start - str_beg, std::move(quoted.content), quoted.terminated};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +404,6 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
|
||||||
|
|
||||||
const StringView line = m_reader.str;
|
const StringView line = m_reader.str;
|
||||||
const char* start = m_reader.pos;
|
const char* start = m_reader.pos;
|
||||||
auto coord = m_reader.coord();
|
|
||||||
|
|
||||||
const char c = *m_reader.pos;
|
const char c = *m_reader.pos;
|
||||||
if (c == '"' or c == '\'')
|
if (c == '"' or c == '\'')
|
||||||
|
@ -404,7 +414,7 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
|
||||||
throw parse_error{format("unterminated string {0}...{0}", c)};
|
throw parse_error{format("unterminated string {0}...{0}", c)};
|
||||||
return Token{c == '"' ? Token::Type::RawEval
|
return Token{c == '"' ? Token::Type::RawEval
|
||||||
: Token::Type::RawQuoted,
|
: Token::Type::RawQuoted,
|
||||||
start - line.begin(), coord, std::move(quoted.content),
|
start - line.begin(), std::move(quoted.content),
|
||||||
quoted.terminated};
|
quoted.terminated};
|
||||||
}
|
}
|
||||||
else if (c == '%')
|
else if (c == '%')
|
||||||
|
@ -416,7 +426,7 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
|
||||||
{
|
{
|
||||||
m_reader.next_byte();
|
m_reader.next_byte();
|
||||||
return Token{Token::Type::CommandSeparator,
|
return Token{Token::Type::CommandSeparator,
|
||||||
m_reader.pos - line.begin(), coord, {}};
|
m_reader.pos - line.begin(), {}};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -427,7 +437,7 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
|
||||||
m_reader.next_byte();
|
m_reader.next_byte();
|
||||||
}
|
}
|
||||||
return Token{Token::Type::Raw, start - line.begin(),
|
return Token{Token::Type::Raw, start - line.begin(),
|
||||||
coord, parse_unquoted(m_reader)};
|
parse_unquoted(m_reader)};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -477,12 +487,6 @@ String expand(StringView str, const Context& context,
|
||||||
return expand_impl(str, context, shell_context, postprocess);
|
return expand_impl(str, context, shell_context, postprocess);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct command_not_found : runtime_error
|
|
||||||
{
|
|
||||||
command_not_found(StringView name)
|
|
||||||
: runtime_error(format("no such command: '{}'", name)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
StringView resolve_alias(const Context& context, StringView name)
|
StringView resolve_alias(const Context& context, StringView name)
|
||||||
{
|
{
|
||||||
auto alias = context.aliases()[name];
|
auto alias = context.aliases()[name];
|
||||||
|
@ -491,8 +495,7 @@ StringView resolve_alias(const Context& context, StringView name)
|
||||||
|
|
||||||
void CommandManager::execute_single_command(CommandParameters params,
|
void CommandManager::execute_single_command(CommandParameters params,
|
||||||
Context& context,
|
Context& context,
|
||||||
const ShellContext& shell_context,
|
const ShellContext& shell_context)
|
||||||
BufferCoord pos)
|
|
||||||
{
|
{
|
||||||
if (params.empty())
|
if (params.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -502,41 +505,25 @@ void CommandManager::execute_single_command(CommandParameters params,
|
||||||
throw runtime_error("maximum nested command depth hit");
|
throw runtime_error("maximum nested command depth hit");
|
||||||
|
|
||||||
++m_command_depth;
|
++m_command_depth;
|
||||||
auto pop_cmd = on_scope_end([this] { --m_command_depth; });
|
auto pop_depth = on_scope_end([this] { --m_command_depth; });
|
||||||
|
|
||||||
ParameterList param_view(params.begin()+1, params.end());
|
|
||||||
auto command_it = m_commands.find(resolve_alias(context, params[0]));
|
auto command_it = m_commands.find(resolve_alias(context, params[0]));
|
||||||
if (command_it == m_commands.end())
|
if (command_it == m_commands.end())
|
||||||
throw command_not_found(params[0]);
|
throw runtime_error("no such command");
|
||||||
|
|
||||||
auto debug_flags = context.options()["debug"].get<DebugFlags>();
|
auto debug_flags = context.options()["debug"].get<DebugFlags>();
|
||||||
auto start = (debug_flags & DebugFlags::Profile) ? Clock::now() : Clock::time_point{};
|
|
||||||
if (debug_flags & DebugFlags::Commands)
|
if (debug_flags & DebugFlags::Commands)
|
||||||
write_to_debug_buffer(format("command {}", join(params, ' ')));
|
write_to_debug_buffer(format("command {}", join(params, ' ')));
|
||||||
|
|
||||||
auto profile = on_scope_end([&] {
|
auto profile = on_scope_end([&, start = (debug_flags & DebugFlags::Profile) ? Clock::now() : Clock::time_point{}] {
|
||||||
if (not (debug_flags & DebugFlags::Profile))
|
if (not (debug_flags & DebugFlags::Profile))
|
||||||
return;
|
return;
|
||||||
auto full = std::chrono::duration_cast<std::chrono::microseconds>(Clock::now() - start);
|
auto full = std::chrono::duration_cast<std::chrono::microseconds>(Clock::now() - start);
|
||||||
write_to_debug_buffer(format("command {} took {} us", params[0], full.count()));
|
write_to_debug_buffer(format("command {} took {} us", params[0], full.count()));
|
||||||
});
|
});
|
||||||
|
|
||||||
try
|
command_it->value.func({{params.begin()+1, params.end()}, command_it->value.param_desc},
|
||||||
{
|
context, shell_context);
|
||||||
ParametersParser parameter_parser(param_view,
|
|
||||||
command_it->value.param_desc);
|
|
||||||
command_it->value.func(parameter_parser, context, shell_context);
|
|
||||||
}
|
|
||||||
catch (failure& error)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (runtime_error& error)
|
|
||||||
{
|
|
||||||
error.set_what(format("{}:{}: '{}' {}", pos.line+1, pos.column+1,
|
|
||||||
params[0], error.what()));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandManager::execute(StringView command_line,
|
void CommandManager::execute(StringView command_line,
|
||||||
|
@ -544,31 +531,50 @@ void CommandManager::execute(StringView command_line,
|
||||||
{
|
{
|
||||||
CommandParser parser(command_line);
|
CommandParser parser(command_line);
|
||||||
|
|
||||||
BufferCoord command_coord;
|
ByteCount command_pos{};
|
||||||
Vector<String, MemoryDomain::Commands> params;
|
Vector<String, MemoryDomain::Commands> params;
|
||||||
while (Optional<Token> token_opt = parser.read_token(true))
|
while (true)
|
||||||
{
|
{
|
||||||
auto& token = *token_opt;
|
Optional<Token> token = parser.read_token(true);
|
||||||
if (params.empty())
|
if (not token or token->type == Token::Type::CommandSeparator)
|
||||||
command_coord = token.coord;
|
|
||||||
|
|
||||||
if (token.type == Token::Type::CommandSeparator)
|
|
||||||
{
|
{
|
||||||
execute_single_command(params, context, shell_context, command_coord);
|
try
|
||||||
params.clear();
|
{
|
||||||
|
execute_single_command(params, context, shell_context);
|
||||||
}
|
}
|
||||||
else if (token.type == Token::Type::ArgExpand and token.content == '@')
|
catch (failure& error)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (runtime_error& error)
|
||||||
|
{
|
||||||
|
auto coord = compute_coord(command_line.substr(0_byte, command_pos));
|
||||||
|
error.set_what(format("{}:{}: '{}': {}", coord.line+1, coord.column+1,
|
||||||
|
params[0], error.what()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not token)
|
||||||
|
return;
|
||||||
|
|
||||||
|
params.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.empty())
|
||||||
|
command_pos = token->pos;
|
||||||
|
|
||||||
|
if (token->type == Token::Type::ArgExpand and token->content == '@')
|
||||||
params.insert(params.end(), shell_context.params.begin(),
|
params.insert(params.end(), shell_context.params.begin(),
|
||||||
shell_context.params.end());
|
shell_context.params.end());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto tokens = expand_token<false>(token, context, shell_context);
|
auto tokens = expand_token<false>(*token, context, shell_context);
|
||||||
params.insert(params.end(),
|
params.insert(params.end(),
|
||||||
std::make_move_iterator(tokens.begin()),
|
std::make_move_iterator(tokens.begin()),
|
||||||
std::make_move_iterator(tokens.end()));
|
std::make_move_iterator(tokens.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
execute_single_command(params, context, shell_context, command_coord);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<CommandInfo> CommandManager::command_info(const Context& context, StringView command_line) const
|
Optional<CommandInfo> CommandManager::command_info(const Context& context, StringView command_line) const
|
||||||
|
@ -673,7 +679,7 @@ Completions CommandManager::complete(const Context& context,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_last_token)
|
if (is_last_token)
|
||||||
tokens.push_back({Token::Type::Raw, command_line.length(), parser.coord(), {}});
|
tokens.push_back({Token::Type::Raw, command_line.length(), {}});
|
||||||
kak_assert(not tokens.empty());
|
kak_assert(not tokens.empty());
|
||||||
const auto& token = tokens.back();
|
const auto& token = tokens.back();
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@ struct Token
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
ByteCount pos;
|
ByteCount pos;
|
||||||
BufferCoord coord;
|
|
||||||
String content;
|
String content;
|
||||||
bool terminated = false;
|
bool terminated = false;
|
||||||
};
|
};
|
||||||
|
@ -65,7 +64,7 @@ struct Token
|
||||||
struct Reader
|
struct Reader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Reader(StringView s) : str{s}, pos{s.begin()}, line_start{s.begin()}, line{} {}
|
Reader(StringView s) : str{s}, pos{s.begin()} {}
|
||||||
|
|
||||||
Codepoint operator*() const;
|
Codepoint operator*() const;
|
||||||
Codepoint peek_next() const;
|
Codepoint peek_next() const;
|
||||||
|
@ -74,12 +73,9 @@ public:
|
||||||
|
|
||||||
explicit operator bool() const { return pos < str.end(); }
|
explicit operator bool() const { return pos < str.end(); }
|
||||||
StringView substr_from(const char* start) const { return {start, pos}; }
|
StringView substr_from(const char* start) const { return {start, pos}; }
|
||||||
BufferCoord coord() const { return {line, (int)(pos - line_start)}; }
|
|
||||||
|
|
||||||
StringView str;
|
StringView str;
|
||||||
const char* pos;
|
const char* pos;
|
||||||
const char* line_start;
|
|
||||||
LineCount line;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandParser
|
class CommandParser
|
||||||
|
@ -89,7 +85,6 @@ public:
|
||||||
Optional<Token> read_token(bool throw_on_unterminated);
|
Optional<Token> read_token(bool throw_on_unterminated);
|
||||||
|
|
||||||
const char* pos() const { return m_reader.pos; }
|
const char* pos() const { return m_reader.pos; }
|
||||||
BufferCoord coord() const { return m_reader.coord(); }
|
|
||||||
bool done() const { return not m_reader; }
|
bool done() const { return not m_reader; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -104,8 +99,7 @@ public:
|
||||||
|
|
||||||
void execute_single_command(CommandParameters params,
|
void execute_single_command(CommandParameters params,
|
||||||
Context& context,
|
Context& context,
|
||||||
const ShellContext& shell_context,
|
const ShellContext& shell_context);
|
||||||
BufferCoord pos = {});
|
|
||||||
|
|
||||||
|
|
||||||
Completions complete(const Context& context, CompletionFlags flags,
|
Completions complete(const Context& context, CompletionFlags flags,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
no such command: 'non-existing-command'
|
1:2: 'non-existing-command': no such command
|
||||||
|
|
Loading…
Reference in New Issue
Block a user