diff --git a/src/parse_grammar.h b/src/parse_grammar.h index 18def39cd..5375a681b 100644 --- a/src/parse_grammar.h +++ b/src/parse_grammar.h @@ -18,6 +18,8 @@ using production_element_t = uint8_t; // Define primitive types. template struct primitive { + using type_tuple = std::tuple<>; + static constexpr parse_token_type_t token = Token; static constexpr production_element_t element() { return Token; } }; @@ -30,6 +32,7 @@ using tok_redirection = primitive; // Define keyword types. template struct keyword { + using type_tuple = std::tuple<>; static constexpr production_element_t element() { // Convert a parse_keyword_t enum to a production_element_t enum. return Keyword + LAST_TOKEN_OR_SYMBOL + 1; @@ -106,7 +109,8 @@ struct alternative { }; // Following are the grammar productions. -#define BODY(T) static constexpr parse_token_type_t symbol = symbol_##T; +#define BODY(T) static constexpr parse_token_type_t token = symbol_##T; + #define DEF(T) struct T : public #define DEF_ALT(T) struct T : public alternative diff --git a/src/parse_tree.h b/src/parse_tree.h index b92f34914..aae621cfb 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -10,6 +10,7 @@ #include #include "common.h" +#include "maybe.h" #include "parse_constants.h" #include "parse_grammar.h" #include "tokenizer.h" @@ -228,6 +229,11 @@ class parse_node_tree_t : public std::vector { bool job_should_be_backgrounded(const parse_node_t &job) const; }; +struct source_range_t { + uint32_t start; + uint32_t length; +}; + /// A helper for type-safe manipulation of parse nodes. /// This is a lightweight value-type class. template @@ -247,7 +253,7 @@ class tnode_t { tnode_t(const parse_node_tree_t *t, const parse_node_t *n) : tree(t), nodeptr(n) { assert(t && "tree cannot be null in this constructor"); - assert((!n || n->type == Type::symbol) && "node has wrong type"); + assert((!n || n->type == Type::token) && "node has wrong type"); } /// Return the underlying (type-erased) node. @@ -256,12 +262,24 @@ class tnode_t { /// Check whether we're populated. explicit operator bool() const { return nodeptr != nullptr; } + bool has_source() const { return nodeptr && nodeptr->has_source(); } + + maybe_t source_range() const { + if (!has_source()) return none(); + return source_range_t{nodeptr->source_start, nodeptr->source_length}; + } + + wcstring get_source(const wcstring &str) const { + assert(has_source() && "Source missing"); + return nodeptr->get_source(str); + } + /// Type-safe access to a child at the given index. template tnode_t> child() const { using child_type = child_at; const parse_node_t *child = nullptr; - if (nodeptr) child = tree->get_child(*nodeptr, Index, child_type::symbol); + if (nodeptr) child = tree->get_child(*nodeptr, Index, child_type::token); return tnode_t{tree, child}; } }; diff --git a/src/reader.cpp b/src/reader.cpp index 806e3d6e7..efbcf7455 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -580,7 +580,8 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso &parse_tree, NULL); // Look for plain statements where the cursor is at the end of the command. - const parse_node_t *matching_cmd_node = NULL; + using namespace grammar; + tnode_t matching_cmd_node; const size_t len = parse_tree.size(); for (size_t i = 0; i < len; i++) { const parse_node_t &node = parse_tree.at(i); @@ -593,12 +594,15 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso continue; // Get the command node. Skip it if we can't or it has no source. - const parse_node_t *cmd_node = parse_tree.get_child(node, 0, parse_token_type_string); - if (cmd_node == NULL || !cmd_node->has_source()) continue; + tnode_t statement(&parse_tree, &node); + tnode_t cmd_node = statement.child<0>(); + + auto msource = cmd_node.source_range(); + if (!msource) continue; // Now see if its source range contains our cursor, including at the end. - if (subcmd_cursor_pos >= cmd_node->source_start && - subcmd_cursor_pos <= cmd_node->source_start + cmd_node->source_length) { + if (subcmd_cursor_pos >= msource->start && + subcmd_cursor_pos <= msource->start + msource->length) { // Success! matching_cmd_node = cmd_node; break; @@ -607,17 +611,16 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso // Now if we found a command node, expand it. bool result = false; - if (matching_cmd_node != NULL) { - assert(matching_cmd_node->type == parse_token_type_string); - const wcstring token = matching_cmd_node->get_source(subcmd); + if (matching_cmd_node) { + const wcstring token = matching_cmd_node.get_source(subcmd); wcstring abbreviation; if (expand_abbreviation(token, &abbreviation)) { // There was an abbreviation! Replace the token in the full command. Maintain the // relative position of the cursor. if (output != NULL) { output->assign(cmdline); - output->replace(subcmd_offset + matching_cmd_node->source_start, - matching_cmd_node->source_length, abbreviation); + source_range_t r = *matching_cmd_node.source_range(); + output->replace(subcmd_offset + r.start, r.length, abbreviation); } result = true; }