static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, node_offset_t node_idx, indent_t node_indent, parse_token_type_t parent_type, bool *has_new_line, wcstring *out_result, bool do_indent) { const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; /* Increment the indent if we are either a root job_list, or root case_item_list, or in an if or while header (#1665) */ const bool is_root_job_list = (node_type == symbol_job_list && parent_type != symbol_job_list); const bool is_root_case_item_list = (node_type == symbol_case_item_list && parent_type != symbol_case_item_list); const bool is_if_while_header = ((node_type == symbol_job || node_type == symbol_andor_job_list) && (parent_type == symbol_if_clause || parent_type == symbol_while_header)); if (is_root_job_list || is_root_case_item_list || is_if_while_header) { node_indent += 1; } /* Handle comments, which come before the text */ if (node.has_comments()) { const parse_node_tree_t::parse_node_list_t comment_nodes = tree.comment_nodes_for_node(node); for (size_t i=0; i < comment_nodes.size(); i++) { const parse_node_t &comment_node = *comment_nodes.at(i); append_whitespace(node_indent, do_indent, *has_new_line, out_result); out_result->append(source, comment_node.source_start, comment_node.source_length); } } if (node_type == parse_token_type_end) { /* Newline */ out_result->push_back(L'\n'); *has_new_line = true; } else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || node_type == parse_special_type_parse_error) { if (node.has_source()) { /* Some type representing a particular token */ append_whitespace(node_indent, do_indent, *has_new_line, out_result); out_result->append(source, node.source_start, node.source_length); *has_new_line = false; } } /* Recurse to all our children */ for (node_offset_t idx = 0; idx < node.child_count; idx++) { /* Note we pass our type to our child, which becomes its parent node type */ prettify_node_recursive(source, tree, node.child_start + idx, node_indent, node_type, has_new_line, out_result, do_indent); } }
int parser_t::eval(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type, parse_node_tree_t tree) { CHECK_BLOCK(1); assert(block_type == TOP || block_type == SUBST); if (tree.empty()) { return 0; } // Determine the initial eval level. If this is the first context, it's -1; otherwise it's the // eval level of the top context. This is sort of wonky because we're stitching together a // global notion of eval level from these separate objects. A better approach would be some // profile object that all contexts share, and that tracks the eval levels on its own. int exec_eval_level = (execution_contexts.empty() ? -1 : execution_contexts.back()->current_eval_level()); // Append to the execution context stack. execution_contexts.push_back( make_unique<parse_execution_context_t>(std::move(tree), cmd, this, exec_eval_level)); const parse_execution_context_t *ctx = execution_contexts.back().get(); // Execute the first node. this->eval_block_node(0, io, block_type); // Clean up the execution context stack. assert(!execution_contexts.empty() && execution_contexts.back().get() == ctx); execution_contexts.pop_back(); return 0; }
// Check if the first argument under the given node is --help static bool first_argument_is_help(const parse_node_tree_t &node_tree, const parse_node_t &node, const wcstring &src) { bool is_help = false; const parse_node_tree_t::parse_node_list_t arg_nodes = node_tree.find_nodes(node, symbol_argument, 1); if (! arg_nodes.empty()) { // Check the first argument only const parse_node_t &arg = *arg_nodes.at(0); const wcstring first_arg_src = arg.get_source(src); is_help = parse_util_argument_is_help(first_arg_src.c_str(), 3); } return is_help; }
/* We are given a parse tree, the index of a node within the tree, its indent, and a vector of indents the same size as the original source string. Set the indent correspdonding to the node's source range, if appropriate. trailing_indent is the indent for nodes with unrealized source, i.e. if I type 'if false <ret>' then we have an if node with an empty job list (without source) but we want the last line to be indented anyways. switch statements also indent. max_visited_node_idx is the largest index we visited. */ static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, int node_indent, parse_token_type_t parent_type, std::vector<int> *indents, int *trailing_indent, node_offset_t *max_visited_node_idx) { /* Guard against incomplete trees */ if (node_idx > tree.size()) return; /* Update max_visited_node_idx */ if (node_idx > *max_visited_node_idx) *max_visited_node_idx = node_idx; /* We could implement this by utilizing the fish grammar. But there's an easy trick instead: almost everything that wraps a job list should be indented by 1. So just find all of the job lists. One exception is switch, which wraps a case_item_list instead of a job_list. The other exception is job_list itself: a job_list is a job and a job_list, and we want that child list to be indented the same as the parent. So just find all job_lists whose parent is not a job_list, and increment their indent by 1. */ const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; /* Increment the indent if we are either a root job_list, or root case_item_list */ const bool is_root_job_list = (node_type == symbol_job_list && parent_type != symbol_job_list); const bool is_root_case_item_list = (node_type == symbol_case_item_list && parent_type != symbol_case_item_list); if (is_root_job_list || is_root_case_item_list) { node_indent += 1; } /* If we have source, store the trailing indent unconditionally. If we do not have source, store the trailing indent only if ours is bigger; this prevents the trailing "run" of terminal job lists from affecting the trailing indent. For example, code like this: if foo will be parsed as this: job_list job if_statement job [if] job_list [empty] job_list [empty] There's two "terminal" job lists, and we want the innermost one. Note we are relying on the fact that nodes are in the same order as the source, i.e. an in-order traversal of the node tree also traverses the source from beginning to end. */ if (node.has_source() || node_indent > *trailing_indent) { *trailing_indent = node_indent; } /* Store the indent into the indent array */ if (node.source_start != SOURCE_OFFSET_INVALID && node.source_start < indents->size()) { if (node.has_source()) { /* A normal non-empty node. Store the indent unconditionally. */ indents->at(node.source_start) = node_indent; } else { /* An empty node. We have a source offset but no source length. This can come about when a node legitimately empty: while true; end The job_list inside the while loop is empty. It still has a source offset (at the end of the while statement) but no source extent. We still need to capture that indent, because there may be comments inside: while true # loop forever end The 'loop forever' comment must be indented, by virtue of storing the indent. Now consider what happens if we remove the end: while true # loop forever Now both the job_list and end_command are unmaterialized. However, we want the indent to be of the job_list and not the end_command. Therefore, we only store the indent if it's bigger. */ if (node_indent > indents->at(node.source_start)) { indents->at(node.source_start) = node_indent; } } } /* Recursive to all our children */ for (node_offset_t idx = 0; idx < node.child_count; idx++) { /* Note we pass our type to our child, which becomes its parent node type */ compute_indents_recursive(tree, node.child_start + idx, node_indent, node_type, indents, trailing_indent, max_visited_node_idx); } }
static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, node_offset_t node_idx, indent_t node_indent, parse_token_type_t parent_type, bool *has_new_line, wcstring *out_result, bool do_indent) { const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; const parse_token_type_t prev_node_type = node_idx > 0 ? tree.at(node_idx - 1).type : token_type_invalid; // Increment the indent if we are either a root job_list, or root case_item_list, or in an if or // while header (#1665). const bool is_root_job_list = node_type == symbol_job_list && parent_type != symbol_job_list; const bool is_root_case_list = node_type == symbol_case_item_list && parent_type != symbol_case_item_list; const bool is_if_while_header = (node_type == symbol_job_conjunction || node_type == symbol_andor_job_list) && (parent_type == symbol_if_clause || parent_type == symbol_while_header); if (is_root_job_list || is_root_case_list || is_if_while_header) { node_indent += 1; } if (dump_parse_tree) dump_node(node_indent, node, source); if (node.has_comments()) // handle comments, which come before the text { auto comment_nodes = tree.comment_nodes_for_node(node); for (const auto &comment : comment_nodes) { append_whitespace(node_indent, do_indent, *has_new_line, out_result); auto source_range = comment.source_range(); out_result->append(source, source_range->start, source_range->length); } } if (node_type == parse_token_type_end) { out_result->push_back(L'\n'); *has_new_line = true; } else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || node_type == parse_special_type_parse_error) { if (node.keyword != parse_keyword_none) { append_whitespace(node_indent, do_indent, *has_new_line, out_result); out_result->append(keyword_description(node.keyword)); *has_new_line = false; } else if (node.has_source()) { // Some type representing a particular token. if (prev_node_type != parse_token_type_redirection) { append_whitespace(node_indent, do_indent, *has_new_line, out_result); } out_result->append(source, node.source_start, node.source_length); *has_new_line = false; } } // Recurse to all our children. for (node_offset_t idx = 0; idx < node.child_count; idx++) { // Note: We pass our type to our child, which becomes its parent node type. // Note: While node.child_start could be -1 (NODE_OFFSET_INVALID) the addition is safe // because we won't execute this call in that case since node.child_count should be zero. prettify_node_recursive(source, tree, node.child_start + idx, node_indent, node_type, has_new_line, out_result, do_indent); } }