mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-03-27 14:45:13 +08:00
Make block_type_t an enum class
This commit is contained in:
parent
4529e7d183
commit
a59f35a378
@ -165,7 +165,7 @@ void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *
|
|||||||
// If it's an error, redirect the output of __fish_print_help to stderr
|
// If it's an error, redirect the output of __fish_print_help to stderr
|
||||||
ios.push_back(std::make_shared<io_fd_t>(STDOUT_FILENO, STDERR_FILENO));
|
ios.push_back(std::make_shared<io_fd_t>(STDOUT_FILENO, STDERR_FILENO));
|
||||||
}
|
}
|
||||||
parser.eval(cmd, ios, TOP);
|
parser.eval(cmd, ios, block_type_t::top);
|
||||||
// ignore the exit status of __fish_print_help
|
// ignore the exit status of __fish_print_help
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,11 +265,11 @@ static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar
|
|||||||
// Paranoia: ensure we have a real loop.
|
// Paranoia: ensure we have a real loop.
|
||||||
bool has_loop = false;
|
bool has_loop = false;
|
||||||
for (const auto &b : parser.blocks()) {
|
for (const auto &b : parser.blocks()) {
|
||||||
if (b.type() == WHILE || b.type() == FOR) {
|
if (b.type() == block_type_t::while_block || b.type() == block_type_t::for_block) {
|
||||||
has_loop = true;
|
has_loop = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (b.is_function()) break;
|
if (b.is_function_call()) break;
|
||||||
}
|
}
|
||||||
if (!has_loop) {
|
if (!has_loop) {
|
||||||
wcstring error_message = format_string(_(L"%ls: Not inside of loop\n"), argv[0]);
|
wcstring error_message = format_string(_(L"%ls: Not inside of loop\n"), argv[0]);
|
||||||
@ -298,7 +298,7 @@ static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t *
|
|||||||
// Ensure we don't allow creating a breakpoint at an interactive prompt. There may be a simpler
|
// Ensure we don't allow creating a breakpoint at an interactive prompt. There may be a simpler
|
||||||
// or clearer way to do this but this works.
|
// or clearer way to do this but this works.
|
||||||
const block_t *block1 = parser.block_at_index(1);
|
const block_t *block1 = parser.block_at_index(1);
|
||||||
if (!block1 || block1->type() == BREAKPOINT) {
|
if (!block1 || block1->type() == block_type_t::breakpoint) {
|
||||||
streams.err.append_format(_(L"%ls: Command not valid at an interactive prompt\n"), cmd);
|
streams.err.append_format(_(L"%ls: Command not valid at an interactive prompt\n"), cmd);
|
||||||
return STATUS_ILLEGAL_CMD;
|
return STATUS_ILLEGAL_CMD;
|
||||||
}
|
}
|
||||||
|
@ -117,8 +117,7 @@ int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UNSET: {
|
case UNSET: {
|
||||||
while (block != nullptr && block->type() != FUNCTION_CALL &&
|
while (block && !block->is_function_call()) {
|
||||||
block->type() != FUNCTION_CALL_NO_SHADOW) {
|
|
||||||
// Set it in function scope
|
// Set it in function scope
|
||||||
block = parser.block_at_index(++block_idx);
|
block = parser.block_at_index(++block_idx);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ int builtin_eval(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||||||
const auto cached_exec_count = parser.libdata().exec_count;
|
const auto cached_exec_count = parser.libdata().exec_count;
|
||||||
int status = STATUS_CMD_OK;
|
int status = STATUS_CMD_OK;
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
if (parser.eval(std::move(new_cmd), *streams.io_chain, block_type_t::TOP) !=
|
if (parser.eval(std::move(new_cmd), *streams.io_chain, block_type_t::top) !=
|
||||||
eval_result_t::ok) {
|
eval_result_t::ok) {
|
||||||
status = STATUS_CMD_ERROR;
|
status = STATUS_CMD_ERROR;
|
||||||
} else if (cached_exec_count == parser.libdata().exec_count) {
|
} else if (cached_exec_count == parser.libdata().exec_count) {
|
||||||
|
@ -93,7 +93,7 @@ int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||||||
// Find the function block.
|
// Find the function block.
|
||||||
bool has_function_block = false;
|
bool has_function_block = false;
|
||||||
for (const auto &b : parser.blocks()) {
|
for (const auto &b : parser.blocks()) {
|
||||||
if (b.is_function()) {
|
if (b.is_function_call()) {
|
||||||
has_function_block = true;
|
has_function_block = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ static void event_fire_internal(parser_t &parser, const event_t &event) {
|
|||||||
auto prev_statuses = parser.get_last_statuses();
|
auto prev_statuses = parser.get_last_statuses();
|
||||||
|
|
||||||
block_t *b = parser.push_block(block_t::event_block(event));
|
block_t *b = parser.push_block(block_t::event_block(event));
|
||||||
parser.eval(buffer, io_chain_t(), TOP);
|
parser.eval(buffer, io_chain_t(), block_type_t::top);
|
||||||
parser.pop_block(b);
|
parser.pop_block(b);
|
||||||
parser.set_last_statuses(std::move(prev_statuses));
|
parser.set_last_statuses(std::move(prev_statuses));
|
||||||
}
|
}
|
||||||
|
@ -733,7 +733,7 @@ static proc_performer_t get_performer_for_process(process_t *p, const job_t *job
|
|||||||
tnode_t<grammar::statement> node = p->internal_block_node;
|
tnode_t<grammar::statement> node = p->internal_block_node;
|
||||||
assert(source && node && "Process is missing node info");
|
assert(source && node && "Process is missing node info");
|
||||||
return [=](parser_t &parser) {
|
return [=](parser_t &parser) {
|
||||||
eval_result_t res = parser.eval_node(source, node, TOP, lineage);
|
eval_result_t res = parser.eval_node(source, node, block_type_t::top, lineage);
|
||||||
switch (res) {
|
switch (res) {
|
||||||
case eval_result_t::ok:
|
case eval_result_t::ok:
|
||||||
case eval_result_t::error:
|
case eval_result_t::error:
|
||||||
@ -758,7 +758,8 @@ static proc_performer_t get_performer_for_process(process_t *p, const job_t *job
|
|||||||
const auto &ld = parser.libdata();
|
const auto &ld = parser.libdata();
|
||||||
auto saved_exec_count = ld.exec_count;
|
auto saved_exec_count = ld.exec_count;
|
||||||
const block_t *fb = function_prepare_environment(parser, *argv, *props);
|
const block_t *fb = function_prepare_environment(parser, *argv, *props);
|
||||||
auto res = parser.eval_node(props->parsed_source, props->body_node, TOP, lineage);
|
auto res = parser.eval_node(props->parsed_source, props->body_node, block_type_t::top,
|
||||||
|
lineage);
|
||||||
function_restore_environment(parser, fb);
|
function_restore_environment(parser, fb);
|
||||||
|
|
||||||
switch (res) {
|
switch (res) {
|
||||||
@ -1175,7 +1176,7 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstrin
|
|||||||
// be null.
|
// be null.
|
||||||
std::shared_ptr<io_buffer_t> buffer;
|
std::shared_ptr<io_buffer_t> buffer;
|
||||||
if (auto bufferfill = io_bufferfill_t::create(fd_set_t{}, ld.read_limit)) {
|
if (auto bufferfill = io_bufferfill_t::create(fd_set_t{}, ld.read_limit)) {
|
||||||
if (parser.eval(cmd, io_chain_t{bufferfill}, SUBST) == eval_result_t::ok) {
|
if (parser.eval(cmd, io_chain_t{bufferfill}, block_type_t::subst) == eval_result_t::ok) {
|
||||||
subcommand_statuses = parser.get_last_statuses();
|
subcommand_statuses = parser.get_last_statuses();
|
||||||
}
|
}
|
||||||
buffer = io_bufferfill_t::finish(std::move(bufferfill));
|
buffer = io_bufferfill_t::finish(std::move(bufferfill));
|
||||||
|
@ -228,7 +228,7 @@ static void source_config_in_directory(const wcstring &dir) {
|
|||||||
const wcstring cmd = L"builtin source " + escaped_pathname;
|
const wcstring cmd = L"builtin source " + escaped_pathname;
|
||||||
parser_t &parser = parser_t::principal_parser();
|
parser_t &parser = parser_t::principal_parser();
|
||||||
set_is_within_fish_initialization(true);
|
set_is_within_fish_initialization(true);
|
||||||
parser.eval(cmd, io_chain_t(), TOP);
|
parser.eval(cmd, io_chain_t(), block_type_t::top);
|
||||||
set_is_within_fish_initialization(false);
|
set_is_within_fish_initialization(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +254,7 @@ int run_command_list(std::vector<std::string> *cmds, const io_chain_t &io) {
|
|||||||
|
|
||||||
for (const auto &cmd : *cmds) {
|
for (const auto &cmd : *cmds) {
|
||||||
const wcstring cmd_wcs = str2wcstring(cmd);
|
const wcstring cmd_wcs = str2wcstring(cmd);
|
||||||
eval_result_t eval_res = parser.eval(cmd_wcs, io, TOP);
|
eval_result_t eval_res = parser.eval(cmd_wcs, io, block_type_t::top);
|
||||||
res = (eval_res == eval_result_t::ok ? 0 : 1);
|
res = (eval_res == eval_result_t::ok ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,16 +1003,17 @@ static void test_parser() {
|
|||||||
// Ensure that we don't crash on infinite self recursion and mutual recursion. These must use
|
// Ensure that we don't crash on infinite self recursion and mutual recursion. These must use
|
||||||
// the principal parser because we cannot yet execute jobs on other parsers.
|
// the principal parser because we cannot yet execute jobs on other parsers.
|
||||||
say(L"Testing recursion detection");
|
say(L"Testing recursion detection");
|
||||||
parser->eval(L"function recursive ; recursive ; end ; recursive; ", io_chain_t(), TOP);
|
parser->eval(L"function recursive ; recursive ; end ; recursive; ", io_chain_t(),
|
||||||
|
block_type_t::top);
|
||||||
#if 0
|
#if 0
|
||||||
// This is disabled since it produces a long backtrace. We should find a way to either visually
|
// This is disabled since it produces a long backtrace. We should find a way to either visually
|
||||||
// compress the backtrace, or disable error spewing.
|
// compress the backtrace, or disable error spewing.
|
||||||
parser->.eval(L"function recursive1 ; recursive2 ; end ; "
|
parser->.eval(L"function recursive1 ; recursive2 ; end ; "
|
||||||
L"function recursive2 ; recursive1 ; end ; recursive1; ", io_chain_t(), TOP);
|
L"function recursive2 ; recursive1 ; end ; recursive1; ", io_chain_t(), block_type_t::top);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
say(L"Testing empty function name");
|
say(L"Testing empty function name");
|
||||||
parser->eval(L"function '' ; echo fail; exit 42 ; end ; ''", io_chain_t(), TOP);
|
parser->eval(L"function '' ; echo fail; exit 42 ; end ; ''", io_chain_t(), block_type_t::top);
|
||||||
|
|
||||||
say(L"Testing eval_args");
|
say(L"Testing eval_args");
|
||||||
completion_list_t comps = parser_t::expand_argument_list(
|
completion_list_t comps = parser_t::expand_argument_list(
|
||||||
@ -1032,7 +1033,8 @@ static void test_1_cancellation(const wchar_t *src) {
|
|||||||
usleep(delay * 1E6);
|
usleep(delay * 1E6);
|
||||||
pthread_kill(thread, SIGINT);
|
pthread_kill(thread, SIGINT);
|
||||||
});
|
});
|
||||||
eval_result_t ret = parser_t::principal_parser().eval(src, io_chain_t{filler}, TOP);
|
eval_result_t ret =
|
||||||
|
parser_t::principal_parser().eval(src, io_chain_t{filler}, block_type_t::top);
|
||||||
auto buffer = io_bufferfill_t::finish(std::move(filler));
|
auto buffer = io_bufferfill_t::finish(std::move(filler));
|
||||||
if (buffer->buffer().size() != 0) {
|
if (buffer->buffer().size() != 0) {
|
||||||
err(L"Expected 0 bytes in out_buff, but instead found %lu bytes, for command %ls\n",
|
err(L"Expected 0 bytes in out_buff, but instead found %lu bytes, for command %ls\n",
|
||||||
@ -1074,7 +1076,7 @@ static void test_cancellation() {
|
|||||||
bool iis = is_interactive_session();
|
bool iis = is_interactive_session();
|
||||||
set_interactive_session(true);
|
set_interactive_session(true);
|
||||||
const wchar_t *child_self_destructor = L"while true ; sh -c 'sleep .25; kill -s INT $$' ; end";
|
const wchar_t *child_self_destructor = L"while true ; sh -c 'sleep .25; kill -s INT $$' ; end";
|
||||||
parser_t::principal_parser().eval(child_self_destructor, io_chain_t(), TOP);
|
parser_t::principal_parser().eval(child_self_destructor, io_chain_t(), block_type_t::top);
|
||||||
set_interactive_session(iis);
|
set_interactive_session(iis);
|
||||||
|
|
||||||
// Restore signal handling.
|
// Restore signal handling.
|
||||||
@ -5198,7 +5200,7 @@ static void test_illegal_command_exit_code() {
|
|||||||
parser_t &parser = parser_t::principal_parser();
|
parser_t &parser = parser_t::principal_parser();
|
||||||
|
|
||||||
for (const auto &test : tests) {
|
for (const auto &test : tests) {
|
||||||
parser.eval(test.txt, empty_ios, TOP);
|
parser.eval(test.txt, empty_ios, block_type_t::top);
|
||||||
|
|
||||||
int exit_status = parser.get_last_status();
|
int exit_status = parser.get_last_status();
|
||||||
if (exit_status != test.result) {
|
if (exit_status != test.result) {
|
||||||
|
@ -389,7 +389,7 @@ void inputter_t::mapping_execute(const input_mapping_t &m, bool allow_commands)
|
|||||||
// see that until all other commands have also been run.
|
// see that until all other commands have also been run.
|
||||||
auto last_statuses = parser_->get_last_statuses();
|
auto last_statuses = parser_->get_last_statuses();
|
||||||
for (const wcstring &cmd : m.commands) {
|
for (const wcstring &cmd : m.commands) {
|
||||||
parser_->eval(cmd, io_chain_t(), TOP);
|
parser_->eval(cmd, io_chain_t(), block_type_t::top);
|
||||||
}
|
}
|
||||||
parser_->set_last_statuses(std::move(last_statuses));
|
parser_->set_last_statuses(std::move(last_statuses));
|
||||||
event_queue_.push_front(char_event_type_t::check_exit);
|
event_queue_.push_front(char_event_type_t::check_exit);
|
||||||
|
@ -105,7 +105,7 @@ tnode_t<g::plain_statement> parse_execution_context_t::infinite_recursive_statem
|
|||||||
// on function invocation changes, then this check will break.
|
// on function invocation changes, then this check will break.
|
||||||
const block_t *current = parser->block_at_index(0), *parent = parser->block_at_index(1);
|
const block_t *current = parser->block_at_index(0), *parent = parser->block_at_index(1);
|
||||||
bool is_within_function_call =
|
bool is_within_function_call =
|
||||||
(current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL);
|
(current && parent && current->type() == block_type_t::top && parent->is_function_call());
|
||||||
if (!is_within_function_call) {
|
if (!is_within_function_call) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -322,7 +322,7 @@ eval_result_t parse_execution_context_t::run_if_statement(tnode_t<g::if_statemen
|
|||||||
eval_result_t parse_execution_context_t::run_begin_statement(tnode_t<g::job_list> contents) {
|
eval_result_t parse_execution_context_t::run_begin_statement(tnode_t<g::job_list> contents) {
|
||||||
// Basic begin/end block. Push a scope block, run jobs, pop it
|
// Basic begin/end block. Push a scope block, run jobs, pop it
|
||||||
trace_if_enabled(*parser, L"begin");
|
trace_if_enabled(*parser, L"begin");
|
||||||
block_t *sb = parser->push_block(block_t::scope_block(BEGIN));
|
block_t *sb = parser->push_block(block_t::scope_block(block_type_t::begin));
|
||||||
eval_result_t ret = run_job_list(contents, sb);
|
eval_result_t ret = run_job_list(contents, sb);
|
||||||
parser->pop_block(sb);
|
parser->pop_block(sb);
|
||||||
trace_if_enabled(*parser, L"end begin");
|
trace_if_enabled(*parser, L"end begin");
|
||||||
@ -380,7 +380,7 @@ bool parse_execution_context_t::is_function_context() const {
|
|||||||
const block_t *current = parser->block_at_index(0);
|
const block_t *current = parser->block_at_index(0);
|
||||||
const block_t *parent = parser->block_at_index(1);
|
const block_t *parent = parser->block_at_index(1);
|
||||||
bool is_within_function_call =
|
bool is_within_function_call =
|
||||||
(current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL);
|
(current && parent && current->type() == block_type_t::top && parent->is_function_call());
|
||||||
return is_within_function_call;
|
return is_within_function_call;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1461,7 +1461,7 @@ eval_result_t parse_execution_context_t::eval_node(tnode_t<g::job_list> job_list
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for stack overflow. The TOP check ensures we only do this for function calls.
|
// Check for stack overflow. The TOP check ensures we only do this for function calls.
|
||||||
if (associated_block->type() == TOP && parser->function_stack_is_overflowing()) {
|
if (associated_block->type() == block_type_t::top && parser->function_stack_is_overflowing()) {
|
||||||
return this->report_error(job_list, CALL_STACK_LIMIT_EXCEEDED_ERR_MSG);
|
return this->report_error(job_list, CALL_STACK_LIMIT_EXCEEDED_ERR_MSG);
|
||||||
}
|
}
|
||||||
return this->run_job_list(job_list, associated_block);
|
return this->run_job_list(job_list, associated_block);
|
||||||
|
160
src/parser.cpp
160
src/parser.cpp
@ -75,33 +75,6 @@ class io_chain_t;
|
|||||||
/// Unknown block description.
|
/// Unknown block description.
|
||||||
#define UNKNOWN_BLOCK N_(L"unknown/invalid block")
|
#define UNKNOWN_BLOCK N_(L"unknown/invalid block")
|
||||||
|
|
||||||
/// Data structure to describe a block type, like while blocks, command substitution blocks, etc.
|
|
||||||
struct block_lookup_entry {
|
|
||||||
// The block type id. The legal values are defined in parser.h.
|
|
||||||
block_type_t type;
|
|
||||||
// The name of the builtin that creates this type of block, if any.
|
|
||||||
const wchar_t *name;
|
|
||||||
// A description of this block type.
|
|
||||||
const wchar_t *desc;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// List of all legal block types.
|
|
||||||
static const struct block_lookup_entry block_lookup[] = {
|
|
||||||
{WHILE, L"while", WHILE_BLOCK},
|
|
||||||
{FOR, L"for", FOR_BLOCK},
|
|
||||||
{IF, L"if", IF_BLOCK},
|
|
||||||
{FUNCTION_CALL, nullptr, FUNCTION_CALL_BLOCK},
|
|
||||||
{FUNCTION_CALL_NO_SHADOW, nullptr, FUNCTION_CALL_NO_SHADOW_BLOCK},
|
|
||||||
{SWITCH, L"switch", SWITCH_BLOCK},
|
|
||||||
{TOP, nullptr, TOP_BLOCK},
|
|
||||||
{SUBST, nullptr, SUBST_BLOCK},
|
|
||||||
{BEGIN, L"begin", BEGIN_BLOCK},
|
|
||||||
{SOURCE, L"source", SOURCE_BLOCK},
|
|
||||||
{EVENT, nullptr, EVENT_BLOCK},
|
|
||||||
{BREAKPOINT, L"breakpoint", BREAKPOINT_BLOCK},
|
|
||||||
{VARIABLE_ASSIGNMENT, L"variable assignment", VARIABLE_ASSIGNMENT_BLOCK},
|
|
||||||
{static_cast<block_type_t>(0), nullptr, nullptr}};
|
|
||||||
|
|
||||||
// Given a file path, return something nicer. Currently we just "unexpand" tildes.
|
// Given a file path, return something nicer. Currently we just "unexpand" tildes.
|
||||||
wcstring parser_t::user_presentable_path(const wcstring &path) const {
|
wcstring parser_t::user_presentable_path(const wcstring &path) const {
|
||||||
return replace_home_directory_with_tilde(path, vars());
|
return replace_home_directory_with_tilde(path, vars());
|
||||||
@ -148,17 +121,18 @@ block_t *parser_t::push_block(block_t &&block) {
|
|||||||
new_current.src_filename = intern(filename);
|
new_current.src_filename = intern(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Types TOP and SUBST are not considered blocks for the purposes of `status is-block`.
|
// Types top and subst are not considered blocks for the purposes of `status is-block`.
|
||||||
if (type != TOP && type != SUBST) {
|
if (type != block_type_t::top && type != block_type_t::subst) {
|
||||||
libdata().is_block = true;
|
libdata().is_block = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == BREAKPOINT) {
|
if (type == block_type_t::breakpoint) {
|
||||||
libdata().is_breakpoint = true;
|
libdata().is_breakpoint = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_current.type() != TOP) {
|
if (new_current.type() != block_type_t::top) {
|
||||||
vars().push(type == FUNCTION_CALL);
|
bool shadow = (type == block_type_t::function_call);
|
||||||
|
vars().push(shadow);
|
||||||
new_current.wants_pop_env = true;
|
new_current.wants_pop_env = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +155,7 @@ void parser_t::pop_block(const block_t *expected) {
|
|||||||
// Figure out if `status is-block` should consider us to be in a block now.
|
// Figure out if `status is-block` should consider us to be in a block now.
|
||||||
bool new_is_block = false;
|
bool new_is_block = false;
|
||||||
for (const auto &b : block_list) {
|
for (const auto &b : block_list) {
|
||||||
if (b.type() != TOP && b.type() != SUBST) {
|
if (b.type() != block_type_t::top && b.type() != block_type_t::subst) {
|
||||||
new_is_block = true;
|
new_is_block = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -191,7 +165,7 @@ void parser_t::pop_block(const block_t *expected) {
|
|||||||
// Are we still in a breakpoint?
|
// Are we still in a breakpoint?
|
||||||
bool new_is_breakpoint = false;
|
bool new_is_breakpoint = false;
|
||||||
for (const auto &b : block_list) {
|
for (const auto &b : block_list) {
|
||||||
if (b.type() == BREAKPOINT) {
|
if (b.type() == block_type_t::breakpoint) {
|
||||||
new_is_breakpoint = true;
|
new_is_breakpoint = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -199,11 +173,35 @@ void parser_t::pop_block(const block_t *expected) {
|
|||||||
libdata().is_breakpoint = new_is_breakpoint;
|
libdata().is_breakpoint = new_is_breakpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *parser_t::get_block_desc(int block) const {
|
const wchar_t *parser_t::get_block_desc(block_type_t block) {
|
||||||
for (size_t i = 0; block_lookup[i].desc; i++) {
|
switch (block) {
|
||||||
if (block_lookup[i].type == block) {
|
case block_type_t::while_block:
|
||||||
return _(block_lookup[i].desc);
|
return WHILE_BLOCK;
|
||||||
}
|
case block_type_t::for_block:
|
||||||
|
return FOR_BLOCK;
|
||||||
|
case block_type_t::if_block:
|
||||||
|
return IF_BLOCK;
|
||||||
|
case block_type_t::function_call:
|
||||||
|
return FUNCTION_CALL_BLOCK;
|
||||||
|
|
||||||
|
case block_type_t::function_call_no_shadow:
|
||||||
|
return FUNCTION_CALL_NO_SHADOW_BLOCK;
|
||||||
|
case block_type_t::switch_block:
|
||||||
|
return SWITCH_BLOCK;
|
||||||
|
case block_type_t::subst:
|
||||||
|
return SUBST_BLOCK;
|
||||||
|
case block_type_t::top:
|
||||||
|
return TOP_BLOCK;
|
||||||
|
case block_type_t::begin:
|
||||||
|
return BEGIN_BLOCK;
|
||||||
|
case block_type_t::source:
|
||||||
|
return SOURCE_BLOCK;
|
||||||
|
case block_type_t::event:
|
||||||
|
return EVENT_BLOCK;
|
||||||
|
case block_type_t::breakpoint:
|
||||||
|
return BREAKPOINT_BLOCK;
|
||||||
|
case block_type_t::variable_assignment:
|
||||||
|
return VARIABLE_ASSIGNMENT_BLOCK;
|
||||||
}
|
}
|
||||||
return _(UNKNOWN_BLOCK);
|
return _(UNKNOWN_BLOCK);
|
||||||
}
|
}
|
||||||
@ -339,7 +337,7 @@ std::shared_ptr<parser_t> parser_t::shared() { return shared_from_this(); }
|
|||||||
wcstring parser_t::stack_trace() const {
|
wcstring parser_t::stack_trace() const {
|
||||||
wcstring trace;
|
wcstring trace;
|
||||||
for (const auto &b : blocks()) {
|
for (const auto &b : blocks()) {
|
||||||
if (b.type() == EVENT) {
|
if (b.type() == block_type_t::event) {
|
||||||
// This is an event handler.
|
// This is an event handler.
|
||||||
assert(b.event && "Should have an event");
|
assert(b.event && "Should have an event");
|
||||||
wcstring description = event_get_desc(*b.event);
|
wcstring description = event_get_desc(*b.event);
|
||||||
@ -353,18 +351,19 @@ wcstring parser_t::stack_trace() const {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b.type() == FUNCTION_CALL || b.type() == FUNCTION_CALL_NO_SHADOW ||
|
if (b.type() == block_type_t::function_call ||
|
||||||
b.type() == SOURCE || b.type() == SUBST) {
|
b.type() == block_type_t::function_call_no_shadow || b.type() == block_type_t::source ||
|
||||||
|
b.type() == block_type_t::subst) {
|
||||||
// These types of blocks should be printed.
|
// These types of blocks should be printed.
|
||||||
switch (b.type()) {
|
switch (b.type()) {
|
||||||
case SOURCE: {
|
case block_type_t::source: {
|
||||||
const wchar_t *source_dest = b.sourced_file;
|
const wchar_t *source_dest = b.sourced_file;
|
||||||
append_format(trace, _(L"from sourcing file %ls\n"),
|
append_format(trace, _(L"from sourcing file %ls\n"),
|
||||||
user_presentable_path(source_dest).c_str());
|
user_presentable_path(source_dest).c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FUNCTION_CALL:
|
case block_type_t::function_call:
|
||||||
case FUNCTION_CALL_NO_SHADOW: {
|
case block_type_t::function_call_no_shadow: {
|
||||||
append_format(trace, _(L"in function '%ls'"), b.function_name.c_str());
|
append_format(trace, _(L"in function '%ls'"), b.function_name.c_str());
|
||||||
// Print arguments on the same line.
|
// Print arguments on the same line.
|
||||||
wcstring args_str;
|
wcstring args_str;
|
||||||
@ -385,7 +384,7 @@ wcstring parser_t::stack_trace() const {
|
|||||||
trace.push_back('\n');
|
trace.push_back('\n');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SUBST: {
|
case block_type_t::subst: {
|
||||||
append_format(trace, _(L"in command substitution\n"));
|
append_format(trace, _(L"in command substitution\n"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -422,9 +421,9 @@ const wchar_t *parser_t::is_function(size_t idx) const {
|
|||||||
|
|
||||||
for (size_t block_idx = idx; block_idx < block_list.size(); block_idx++) {
|
for (size_t block_idx = idx; block_idx < block_list.size(); block_idx++) {
|
||||||
const block_t &b = block_list[block_idx];
|
const block_t &b = block_list[block_idx];
|
||||||
if (b.is_function()) {
|
if (b.is_function_call()) {
|
||||||
return b.function_name.c_str();
|
return b.function_name.c_str();
|
||||||
} else if (b.type() == SOURCE) {
|
} else if (b.type() == block_type_t::source) {
|
||||||
// If a function sources a file, obviously that function's offset doesn't
|
// If a function sources a file, obviously that function's offset doesn't
|
||||||
// contribute.
|
// contribute.
|
||||||
break;
|
break;
|
||||||
@ -442,9 +441,9 @@ const wchar_t *parser_t::get_function_name(int level) {
|
|||||||
// Walk until we find a breakpoint, then take the next function.
|
// Walk until we find a breakpoint, then take the next function.
|
||||||
bool found_breakpoint = false;
|
bool found_breakpoint = false;
|
||||||
for (const auto &b : block_list) {
|
for (const auto &b : block_list) {
|
||||||
if (b.type() == BREAKPOINT) {
|
if (b.type() == block_type_t::breakpoint) {
|
||||||
found_breakpoint = true;
|
found_breakpoint = true;
|
||||||
} else if (found_breakpoint && b.is_function()) {
|
} else if (found_breakpoint && b.is_function_call()) {
|
||||||
return b.function_name.c_str();
|
return b.function_name.c_str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,7 +456,7 @@ const wchar_t *parser_t::get_function_name(int level) {
|
|||||||
// Level 1 is the topmost function call. Level 2 is its caller. Etc.
|
// Level 1 is the topmost function call. Level 2 is its caller. Etc.
|
||||||
int funcs_seen = 0;
|
int funcs_seen = 0;
|
||||||
for (const auto &b : block_list) {
|
for (const auto &b : block_list) {
|
||||||
if (b.is_function()) {
|
if (b.is_function_call()) {
|
||||||
funcs_seen++;
|
funcs_seen++;
|
||||||
if (funcs_seen == level) {
|
if (funcs_seen == level) {
|
||||||
return b.function_name.c_str();
|
return b.function_name.c_str();
|
||||||
@ -479,9 +478,9 @@ const wchar_t *parser_t::current_filename() const {
|
|||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
|
||||||
for (const auto &b : block_list) {
|
for (const auto &b : block_list) {
|
||||||
if (b.is_function()) {
|
if (b.is_function_call()) {
|
||||||
return function_get_definition_file(b.function_name);
|
return function_get_definition_file(b.function_name);
|
||||||
} else if (b.type() == SOURCE) {
|
} else if (b.type() == block_type_t::source) {
|
||||||
return b.sourced_file;
|
return b.sourced_file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -499,7 +498,7 @@ bool parser_t::function_stack_is_overflowing() const {
|
|||||||
// Count the functions.
|
// Count the functions.
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
for (const auto &b : block_list) {
|
for (const auto &b : block_list) {
|
||||||
depth += b.is_function();
|
depth += b.is_function_call();
|
||||||
}
|
}
|
||||||
return depth > FISH_MAX_STACK_DEPTH;
|
return depth > FISH_MAX_STACK_DEPTH;
|
||||||
}
|
}
|
||||||
@ -620,7 +619,7 @@ eval_result_t parser_t::eval(const wcstring &cmd, const io_chain_t &io,
|
|||||||
|
|
||||||
eval_result_t parser_t::eval(parsed_source_ref_t ps, const io_chain_t &io,
|
eval_result_t parser_t::eval(parsed_source_ref_t ps, const io_chain_t &io,
|
||||||
enum block_type_t block_type) {
|
enum block_type_t block_type) {
|
||||||
assert(block_type == TOP || block_type == SUBST);
|
assert(block_type == block_type_t::top || block_type == block_type_t::subst);
|
||||||
if (!ps->tree.empty()) {
|
if (!ps->tree.empty()) {
|
||||||
job_lineage_t lineage;
|
job_lineage_t lineage;
|
||||||
lineage.block_io = io;
|
lineage.block_io = io;
|
||||||
@ -648,7 +647,8 @@ eval_result_t parser_t::eval_node(parsed_source_ref_t ps, tnode_t<T> node, block
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only certain blocks are allowed.
|
// Only certain blocks are allowed.
|
||||||
assert((block_type == TOP || block_type == SUBST) && "Invalid block type");
|
assert((block_type == block_type_t::top || block_type == block_type_t::subst) &&
|
||||||
|
"Invalid block type");
|
||||||
|
|
||||||
job_reap(*this, false); // not sure why we reap jobs here
|
job_reap(*this, false); // not sure why we reap jobs here
|
||||||
|
|
||||||
@ -728,55 +728,55 @@ block_t::~block_t() = default;
|
|||||||
wcstring block_t::description() const {
|
wcstring block_t::description() const {
|
||||||
wcstring result;
|
wcstring result;
|
||||||
switch (this->type()) {
|
switch (this->type()) {
|
||||||
case WHILE: {
|
case block_type_t::while_block: {
|
||||||
result.append(L"while");
|
result.append(L"while");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FOR: {
|
case block_type_t::for_block: {
|
||||||
result.append(L"for");
|
result.append(L"for");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IF: {
|
case block_type_t::if_block: {
|
||||||
result.append(L"if");
|
result.append(L"if");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FUNCTION_CALL: {
|
case block_type_t::function_call: {
|
||||||
result.append(L"function_call");
|
result.append(L"function_call");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FUNCTION_CALL_NO_SHADOW: {
|
case block_type_t::function_call_no_shadow: {
|
||||||
result.append(L"function_call_no_shadow");
|
result.append(L"function_call_no_shadow");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SWITCH: {
|
case block_type_t::switch_block: {
|
||||||
result.append(L"switch");
|
result.append(L"switch");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SUBST: {
|
case block_type_t::subst: {
|
||||||
result.append(L"substitution");
|
result.append(L"substitution");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TOP: {
|
case block_type_t::top: {
|
||||||
result.append(L"top");
|
result.append(L"top");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BEGIN: {
|
case block_type_t::begin: {
|
||||||
result.append(L"begin");
|
result.append(L"begin");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SOURCE: {
|
case block_type_t::source: {
|
||||||
result.append(L"source");
|
result.append(L"source");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EVENT: {
|
case block_type_t::event: {
|
||||||
result.append(L"event");
|
result.append(L"event");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BREAKPOINT: {
|
case block_type_t::breakpoint: {
|
||||||
result.append(L"breakpoint");
|
result.append(L"breakpoint");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VARIABLE_ASSIGNMENT: {
|
case block_type_t::variable_assignment: {
|
||||||
result.append(L"variable_assignment");
|
result.append(L"variable_assignment");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -793,33 +793,35 @@ wcstring block_t::description() const {
|
|||||||
|
|
||||||
// Various block constructors.
|
// Various block constructors.
|
||||||
|
|
||||||
block_t block_t::if_block() { return block_t(IF); }
|
block_t block_t::if_block() { return block_t(block_type_t::if_block); }
|
||||||
|
|
||||||
block_t block_t::event_block(event_t evt) {
|
block_t block_t::event_block(event_t evt) {
|
||||||
block_t b{EVENT};
|
block_t b{block_type_t::event};
|
||||||
b.event = std::move(evt);
|
b.event = std::move(evt);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
block_t block_t::function_block(wcstring name, wcstring_list_t args, bool shadows) {
|
block_t block_t::function_block(wcstring name, wcstring_list_t args, bool shadows) {
|
||||||
block_t b{shadows ? FUNCTION_CALL : FUNCTION_CALL_NO_SHADOW};
|
block_t b{shadows ? block_type_t::function_call : block_type_t::function_call_no_shadow};
|
||||||
b.function_name = std::move(name);
|
b.function_name = std::move(name);
|
||||||
b.function_args = std::move(args);
|
b.function_args = std::move(args);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
block_t block_t::source_block(const wchar_t *src) {
|
block_t block_t::source_block(const wchar_t *src) {
|
||||||
block_t b{SOURCE};
|
block_t b{block_type_t::source};
|
||||||
b.sourced_file = src;
|
b.sourced_file = src;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
block_t block_t::for_block() { return block_t{FOR}; }
|
block_t block_t::for_block() { return block_t{block_type_t::for_block}; }
|
||||||
block_t block_t::while_block() { return block_t{WHILE}; }
|
block_t block_t::while_block() { return block_t{block_type_t::while_block}; }
|
||||||
block_t block_t::switch_block() { return block_t{SWITCH}; }
|
block_t block_t::switch_block() { return block_t{block_type_t::switch_block}; }
|
||||||
block_t block_t::scope_block(block_type_t type) {
|
block_t block_t::scope_block(block_type_t type) {
|
||||||
assert((type == BEGIN || type == TOP || type == SUBST) && "Invalid scope type");
|
assert(
|
||||||
|
(type == block_type_t::begin || type == block_type_t::top || type == block_type_t::subst) &&
|
||||||
|
"Invalid scope type");
|
||||||
return block_t(type);
|
return block_t(type);
|
||||||
}
|
}
|
||||||
block_t block_t::breakpoint_block() { return block_t(BREAKPOINT); }
|
block_t block_t::breakpoint_block() { return block_t(block_type_t::breakpoint); }
|
||||||
block_t block_t::variable_assignment_block() { return block_t(VARIABLE_ASSIGNMENT); }
|
block_t block_t::variable_assignment_block() { return block_t(block_type_t::variable_assignment); }
|
||||||
|
35
src/parser.h
35
src/parser.h
@ -31,20 +31,20 @@ inline bool event_block_list_blocks_type(const event_blockage_list_t &ebls) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Types of blocks.
|
/// Types of blocks.
|
||||||
enum block_type_t {
|
enum class block_type_t {
|
||||||
WHILE, /// While loop block
|
while_block, /// While loop block
|
||||||
FOR, /// For loop block
|
for_block, /// For loop block
|
||||||
IF, /// If block
|
if_block, /// If block
|
||||||
FUNCTION_CALL, /// Function invocation block
|
function_call, /// Function invocation block
|
||||||
FUNCTION_CALL_NO_SHADOW, /// Function invocation block with no variable shadowing
|
function_call_no_shadow, /// Function invocation block with no variable shadowing
|
||||||
SWITCH, /// Switch block
|
switch_block, /// Switch block
|
||||||
SUBST, /// Command substitution scope
|
subst, /// Command substitution scope
|
||||||
TOP, /// Outermost block
|
top, /// Outermost block
|
||||||
BEGIN, /// Unconditional block
|
begin, /// Unconditional block
|
||||||
SOURCE, /// Block created by the . (source) builtin
|
source, /// Block created by the . (source) builtin
|
||||||
EVENT, /// Block created on event notifier invocation
|
event, /// Block created on event notifier invocation
|
||||||
BREAKPOINT, /// Breakpoint block
|
breakpoint, /// Breakpoint block
|
||||||
VARIABLE_ASSIGNMENT, /// Variable assignment before a command
|
variable_assignment, /// Variable assignment before a command
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Possible states for a loop.
|
/// Possible states for a loop.
|
||||||
@ -91,8 +91,9 @@ class block_t {
|
|||||||
wcstring description() const;
|
wcstring description() const;
|
||||||
|
|
||||||
/// \return if we are a function call (with or without shadowing).
|
/// \return if we are a function call (with or without shadowing).
|
||||||
bool is_function() const {
|
bool is_function_call() const {
|
||||||
return type() == FUNCTION_CALL || type() == FUNCTION_CALL_NO_SHADOW;
|
return type() == block_type_t::function_call ||
|
||||||
|
type() == block_type_t::function_call_no_shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Entry points for creating blocks.
|
/// Entry points for creating blocks.
|
||||||
@ -329,7 +330,7 @@ class parser_t : public std::enable_shared_from_this<parser_t> {
|
|||||||
void pop_block(const block_t *expected);
|
void pop_block(const block_t *expected);
|
||||||
|
|
||||||
/// Return a description of the given blocktype.
|
/// Return a description of the given blocktype.
|
||||||
const wchar_t *get_block_desc(int block) const;
|
static const wchar_t *get_block_desc(block_type_t block);
|
||||||
|
|
||||||
/// Return the function name for the specified stack frame. Default is one (current frame).
|
/// Return the function name for the specified stack frame. Default is one (current frame).
|
||||||
const wchar_t *get_function_name(int level = 1);
|
const wchar_t *get_function_name(int level = 1);
|
||||||
|
@ -1952,7 +1952,7 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) {
|
|||||||
|
|
||||||
gettimeofday(&time_before, nullptr);
|
gettimeofday(&time_before, nullptr);
|
||||||
|
|
||||||
parser.eval(cmd, io_chain_t(), TOP);
|
parser.eval(cmd, io_chain_t(), block_type_t::top);
|
||||||
job_reap(parser, true);
|
job_reap(parser, true);
|
||||||
|
|
||||||
gettimeofday(&time_after, nullptr);
|
gettimeofday(&time_after, nullptr);
|
||||||
@ -2205,7 +2205,7 @@ void reader_bg_job_warning(const parser_t &parser) {
|
|||||||
static void handle_end_loop(const parser_t &parser) {
|
static void handle_end_loop(const parser_t &parser) {
|
||||||
if (!reader_exit_forced()) {
|
if (!reader_exit_forced()) {
|
||||||
for (const auto &b : parser.blocks()) {
|
for (const auto &b : parser.blocks()) {
|
||||||
if (b.type() == BREAKPOINT) {
|
if (b.type() == block_type_t::breakpoint) {
|
||||||
// We're executing within a breakpoint so we do not want to terminate the shell and
|
// We're executing within a breakpoint so we do not want to terminate the shell and
|
||||||
// kill background jobs.
|
// kill background jobs.
|
||||||
return;
|
return;
|
||||||
@ -3551,7 +3551,7 @@ static int read_ni(parser_t &parser, int fd, const io_chain_t &io) {
|
|||||||
parsed_source_ref_t pstree;
|
parsed_source_ref_t pstree;
|
||||||
if (!parse_util_detect_errors(str, &errors, false /* do not accept incomplete */,
|
if (!parse_util_detect_errors(str, &errors, false /* do not accept incomplete */,
|
||||||
&pstree)) {
|
&pstree)) {
|
||||||
parser.eval(pstree, io, TOP);
|
parser.eval(pstree, io, block_type_t::top);
|
||||||
} else {
|
} else {
|
||||||
wcstring sb;
|
wcstring sb;
|
||||||
parser.get_backtrace(str, errors, sb);
|
parser.get_backtrace(str, errors, sb);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user