Beispiel #1
0
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);
    }
}
Beispiel #2
0
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;
}
Beispiel #3
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;
}
Beispiel #4
0
/* 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);
    }
}
Beispiel #5
0
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);
    }
}