aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorTom Tromey <tromey@adacore.com>2022-02-22 11:18:01 -0700
committerTom Tromey <tromey@adacore.com>2022-04-04 12:46:09 -0600
commitc66ed94ae961c19b0d3028489d00a2df5a949aac (patch)
tree9a61add0242fd5914ed0ba3578bcbcb90fe76dcc /gdb
parent1e237aba2216f89b9a4b3235ad8d09d1b1b8f039 (diff)
downloadgdb-c66ed94ae961c19b0d3028489d00a2df5a949aac.zip
gdb-c66ed94ae961c19b0d3028489d00a2df5a949aac.tar.gz
gdb-c66ed94ae961c19b0d3028489d00a2df5a949aac.tar.bz2
Implement completion for Ada attributes
This adds a completer for Ada attributes. Some work in the lexer is required in order to match end-of-input correctly, as flex does not have a general-purpose way of doing this. (The approach taken here is recommended in the flex manual.)
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ada-exp.y31
-rw-r--r--gdb/ada-lex.l71
-rw-r--r--gdb/parser-defs.h8
-rw-r--r--gdb/testsuite/gdb.ada/formatted_ref.exp5
4 files changed, 103 insertions, 12 deletions
diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index ebf3925..204e77a 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -393,6 +393,30 @@ pop_associations (int n)
return result;
}
+/* Expression completer for attributes. */
+struct ada_tick_completer : public expr_completion_base
+{
+ explicit ada_tick_completer (std::string &&name)
+ : m_name (std::move (name))
+ {
+ }
+
+ bool complete (struct expression *exp,
+ completion_tracker &tracker) override;
+
+private:
+
+ std::string m_name;
+};
+
+/* Make a new ada_tick_completer and wrap it in a unique pointer. */
+static std::unique_ptr<expr_completion_base>
+make_tick_completer (struct stoken tok)
+{
+ return (std::unique_ptr<expr_completion_base>
+ (new ada_tick_completer (std::string (tok.ptr, tok.length))));
+}
+
%}
%union
@@ -420,7 +444,7 @@ pop_associations (int n)
%token <typed_val_float> FLOAT
%token TRUEKEYWORD FALSEKEYWORD
%token COLONCOLON
-%token <sval> STRING NAME DOT_ID
+%token <sval> STRING NAME DOT_ID TICK_COMPLETE
%type <bval> block
%type <lval> arglist tick_arglist
@@ -449,6 +473,7 @@ pop_associations (int n)
%right TICK_ACCESS TICK_ADDRESS TICK_FIRST TICK_LAST TICK_LENGTH
%right TICK_MAX TICK_MIN TICK_MODULUS
%right TICK_POS TICK_RANGE TICK_SIZE TICK_TAG TICK_VAL
+%right TICK_COMPLETE
/* The following are right-associative only so that reductions at this
precedence have lower precedence than '.' and '('. The syntax still
forces a.b.c, e.g., to be LEFT-associated. */
@@ -784,6 +809,10 @@ primary : primary TICK_ACCESS
{ ada_addrof (); }
| primary TICK_ADDRESS
{ ada_addrof (type_system_address (pstate)); }
+ | primary TICK_COMPLETE
+ {
+ pstate->mark_completion (make_tick_completer ($2));
+ }
| primary TICK_FIRST tick_arglist
{
operation_up arg = ada_pop ();
diff --git a/gdb/ada-lex.l b/gdb/ada-lex.l
index c6ce1ae..6c32a9b 100644
--- a/gdb/ada-lex.l
+++ b/gdb/ada-lex.l
@@ -39,6 +39,11 @@ OPER ([-+*/=<>&]|"<="|">="|"**"|"/="|"and"|"or"|"xor"|"not"|"mod"|"rem"|"abs"
EXP (e[+-]{NUM10})
POSEXP (e"+"?{NUM10})
+/* This must agree with COMPLETION_CHAR below. See the comment there
+ for the explanation. */
+COMPLETE "\001"
+NOT_COMPLETE [^\001]
+
%{
#include "diagnostics.h"
@@ -73,16 +78,35 @@ static void rewind_to_char (int);
Defining YY_NO_INPUT comments it out. */
#define YY_NO_INPUT
+/* When completing, we'll return a special character at the end of the
+ input, to signal the completion position to the lexer. This is
+ done because flex does not have a generally useful way to detect
+ EOF in a pattern. This variable records whether the special
+ character has been emitted. */
+static bool returned_complete = false;
+
+/* The character we use to represent the completion point. */
+#define COMPLETE_CHAR '\001'
+
#undef YY_INPUT
-#define YY_INPUT(BUF, RESULT, MAX_SIZE) \
- if ( *pstate->lexptr == '\000' ) \
- (RESULT) = YY_NULL; \
- else \
- { \
- *(BUF) = *pstate->lexptr; \
- (RESULT) = 1; \
- pstate->lexptr += 1; \
- }
+#define YY_INPUT(BUF, RESULT, MAX_SIZE) \
+ if ( *pstate->lexptr == '\000' ) \
+ { \
+ if (pstate->parse_completion && !returned_complete) \
+ { \
+ returned_complete = true; \
+ *(BUF) = COMPLETE_CHAR; \
+ (RESULT) = 1; \
+ } \
+ else \
+ (RESULT) = YY_NULL; \
+ } \
+ else \
+ { \
+ *(BUF) = *pstate->lexptr == COMPLETE_CHAR ? ' ' : *pstate->lexptr; \
+ (RESULT) = 1; \
+ pstate->lexptr += 1; \
+ }
static int find_dot_all (const char *);
@@ -227,7 +251,7 @@ false { return FALSEKEYWORD; }
/* ATTRIBUTES */
-{TICK}[a-z][a-z_]+ { BEGIN INITIAL; return processAttribute (yytext); }
+{TICK}([a-z][a-z_]*)?{COMPLETE}? { BEGIN INITIAL; return processAttribute (yytext); }
/* PUNCTUATION */
@@ -239,7 +263,7 @@ false { return FALSEKEYWORD; }
"<=" { return LEQ; }
">=" { return GEQ; }
-<BEFORE_QUAL_QUOTE>"'" { BEGIN INITIAL; return '\''; }
+<BEFORE_QUAL_QUOTE>"'"/{NOT_COMPLETE} { BEGIN INITIAL; return '\''; }
[-&*+./:<>=|;\[\]] { return yytext[0]; }
@@ -320,6 +344,7 @@ lexer_init (FILE *inp)
{
BEGIN INITIAL;
paren_depth = 0;
+ returned_complete = false;
yyrestart (inp);
}
@@ -668,6 +693,16 @@ processAttribute (const char *str)
while (isspace (*str))
++str;
+ int len = strlen (str);
+ if (len > 0 && str[len - 1] == COMPLETE_CHAR)
+ {
+ /* This is enforced by YY_INPUT. */
+ gdb_assert (pstate->parse_completion);
+ yylval.sval.ptr = obstack_strndup (&temp_parse_space, str, len - 1);
+ yylval.sval.length = len - 1;
+ return TICK_COMPLETE;
+ }
+
for (const auto &item : attributes)
if (strcasecmp (str, item.name) == 0)
return item.code;
@@ -687,6 +722,20 @@ processAttribute (const char *str)
return *found;
}
+bool
+ada_tick_completer::complete (struct expression *exp,
+ completion_tracker &tracker)
+{
+ completion_list output;
+ for (const auto &item : attributes)
+ {
+ if (strncasecmp (item.name, m_name.c_str (), m_name.length ()) == 0)
+ output.emplace_back (xstrdup (item.name));
+ }
+ tracker.add_completions (std::move (output));
+ return true;
+}
+
/* Back up lexptr by yyleng and then to the rightmost occurrence of
character CH, case-folded (there must be one). WARNING: since
lexptr points to the next input character that Flex has not yet
diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h
index 71381b1..3be7d6c 100644
--- a/gdb/parser-defs.h
+++ b/gdb/parser-defs.h
@@ -195,6 +195,14 @@ struct parser_state : public expr_builder
void mark_completion_tag (enum type_code tag, const char *ptr, int length);
+ /* Mark for completion, using an arbitrary completer. */
+
+ void mark_completion (std::unique_ptr<expr_completion_base> completer)
+ {
+ gdb_assert (m_completion_state == nullptr);
+ m_completion_state = std::move (completer);
+ }
+
/* Push an operation on the stack. */
void push (expr::operation_up &&op)
{
diff --git a/gdb/testsuite/gdb.ada/formatted_ref.exp b/gdb/testsuite/gdb.ada/formatted_ref.exp
index 882dbf1..19a3265 100644
--- a/gdb/testsuite/gdb.ada/formatted_ref.exp
+++ b/gdb/testsuite/gdb.ada/formatted_ref.exp
@@ -82,6 +82,11 @@ proc test_p_x_addr { var addr } {
}
}
}
+
+ gdb_test "complete print/x $var'unres" "print/x $var'unrestricted_access"
+ gdb_test_no_output "complete print/x $var'abcd"
+ gdb_test "complete print $var'f" "print $var'first"
+
return 0
}