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:
Maxime Coste 2021-06-24 16:42:45 +10:00
parent 49e2ecdcdb
commit be9b2de0ee
3 changed files with 70 additions and 70 deletions

View File

@ -101,12 +101,6 @@ Codepoint Reader::peek_next() const
Reader& Reader::operator++()
{
kak_assert(pos < str.end());
if (*pos == '\n')
{
++line;
line_start = ++pos;
return *this;
}
utf8::to_next(pos, str.end());
return *this;
}
@ -114,11 +108,7 @@ Reader& Reader::operator++()
Reader& Reader::next_byte()
{
kak_assert(pos < str.end());
if (*pos++ == '\n')
{
++line;
line_start = pos;
}
++pos;
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)
{
kak_assert(*reader == '%');
@ -278,7 +284,6 @@ Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
};
const Codepoint opening_delimiter = *reader;
auto coord = reader.coord();
++reader;
auto start = reader.pos;
@ -291,22 +296,28 @@ Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
const Codepoint closing_delimiter = it->closing;
auto quoted = parse_quoted_balanced(reader, opening_delimiter, closing_delimiter);
if (throw_on_unterminated and not quoted.terminated)
{
auto coord = compute_coord({reader.str.begin(), start});
throw parse_error{format("{}:{}: unterminated string '%{}{}...{}'",
coord.line, coord.column, type_name,
coord.line+1, coord.column+1, type_name,
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
{
auto quoted = parse_quoted(reader, opening_delimiter);
if (throw_on_unterminated and not quoted.terminated)
{
auto coord = compute_coord({reader.str.begin(), start});
throw parse_error{format("{}:{}: unterminated string '%{}{}...{}'",
coord.line, coord.column, type_name,
coord.line+1, coord.column+1, type_name,
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 char* start = m_reader.pos;
auto coord = m_reader.coord();
const char c = *m_reader.pos;
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)};
return Token{c == '"' ? Token::Type::RawEval
: Token::Type::RawQuoted,
start - line.begin(), coord, std::move(quoted.content),
start - line.begin(), std::move(quoted.content),
quoted.terminated};
}
else if (c == '%')
@ -416,7 +426,7 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
{
m_reader.next_byte();
return Token{Token::Type::CommandSeparator,
m_reader.pos - line.begin(), coord, {}};
m_reader.pos - line.begin(), {}};
}
else
{
@ -427,7 +437,7 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
m_reader.next_byte();
}
return Token{Token::Type::Raw, start - line.begin(),
coord, parse_unquoted(m_reader)};
parse_unquoted(m_reader)};
}
return {};
}
@ -477,12 +487,6 @@ String expand(StringView str, const Context& context,
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)
{
auto alias = context.aliases()[name];
@ -491,8 +495,7 @@ StringView resolve_alias(const Context& context, StringView name)
void CommandManager::execute_single_command(CommandParameters params,
Context& context,
const ShellContext& shell_context,
BufferCoord pos)
const ShellContext& shell_context)
{
if (params.empty())
return;
@ -502,41 +505,25 @@ void CommandManager::execute_single_command(CommandParameters params,
throw runtime_error("maximum nested command depth hit");
++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]));
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 start = (debug_flags & DebugFlags::Profile) ? Clock::now() : Clock::time_point{};
if (debug_flags & DebugFlags::Commands)
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))
return;
auto full = std::chrono::duration_cast<std::chrono::microseconds>(Clock::now() - start);
write_to_debug_buffer(format("command {} took {} us", params[0], full.count()));
});
try
{
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;
}
command_it->value.func({{params.begin()+1, params.end()}, command_it->value.param_desc},
context, shell_context);
}
void CommandManager::execute(StringView command_line,
@ -544,31 +531,50 @@ void CommandManager::execute(StringView command_line,
{
CommandParser parser(command_line);
BufferCoord command_coord;
ByteCount command_pos{};
Vector<String, MemoryDomain::Commands> params;
while (Optional<Token> token_opt = parser.read_token(true))
while (true)
{
auto& token = *token_opt;
if (params.empty())
command_coord = token.coord;
if (token.type == Token::Type::CommandSeparator)
Optional<Token> token = parser.read_token(true);
if (not token or token->type == Token::Type::CommandSeparator)
{
execute_single_command(params, context, shell_context, command_coord);
params.clear();
try
{
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(),
shell_context.params.end());
else
{
auto tokens = expand_token<false>(token, context, shell_context);
auto tokens = expand_token<false>(*token, context, shell_context);
params.insert(params.end(),
std::make_move_iterator(tokens.begin()),
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
@ -673,7 +679,7 @@ Completions CommandManager::complete(const Context& context,
}
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());
const auto& token = tokens.back();

View File

@ -57,7 +57,6 @@ struct Token
Type type;
ByteCount pos;
BufferCoord coord;
String content;
bool terminated = false;
};
@ -65,7 +64,7 @@ struct Token
struct Reader
{
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 peek_next() const;
@ -74,12 +73,9 @@ public:
explicit operator bool() const { return pos < str.end(); }
StringView substr_from(const char* start) const { return {start, pos}; }
BufferCoord coord() const { return {line, (int)(pos - line_start)}; }
StringView str;
const char* pos;
const char* line_start;
LineCount line;
};
class CommandParser
@ -89,7 +85,6 @@ public:
Optional<Token> read_token(bool throw_on_unterminated);
const char* pos() const { return m_reader.pos; }
BufferCoord coord() const { return m_reader.coord(); }
bool done() const { return not m_reader; }
private:
@ -104,8 +99,7 @@ public:
void execute_single_command(CommandParameters params,
Context& context,
const ShellContext& shell_context,
BufferCoord pos = {});
const ShellContext& shell_context);
Completions complete(const Context& context, CompletionFlags flags,

View File

@ -1 +1 @@
no such command: 'non-existing-command'
1:2: 'non-existing-command': no such command