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.)
This commit is contained in:
Tom Tromey 2022-02-22 11:18:01 -07:00
parent 1e237aba22
commit c66ed94ae9
4 changed files with 103 additions and 12 deletions

View file

@ -393,6 +393,30 @@ pop_associations (int n)
return result; 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 %union
@ -420,7 +444,7 @@ pop_associations (int n)
%token <typed_val_float> FLOAT %token <typed_val_float> FLOAT
%token TRUEKEYWORD FALSEKEYWORD %token TRUEKEYWORD FALSEKEYWORD
%token COLONCOLON %token COLONCOLON
%token <sval> STRING NAME DOT_ID %token <sval> STRING NAME DOT_ID TICK_COMPLETE
%type <bval> block %type <bval> block
%type <lval> arglist tick_arglist %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_ACCESS TICK_ADDRESS TICK_FIRST TICK_LAST TICK_LENGTH
%right TICK_MAX TICK_MIN TICK_MODULUS %right TICK_MAX TICK_MIN TICK_MODULUS
%right TICK_POS TICK_RANGE TICK_SIZE TICK_TAG TICK_VAL %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 /* The following are right-associative only so that reductions at this
precedence have lower precedence than '.' and '('. The syntax still precedence have lower precedence than '.' and '('. The syntax still
forces a.b.c, e.g., to be LEFT-associated. */ forces a.b.c, e.g., to be LEFT-associated. */
@ -784,6 +809,10 @@ primary : primary TICK_ACCESS
{ ada_addrof (); } { ada_addrof (); }
| primary TICK_ADDRESS | primary TICK_ADDRESS
{ ada_addrof (type_system_address (pstate)); } { ada_addrof (type_system_address (pstate)); }
| primary TICK_COMPLETE
{
pstate->mark_completion (make_tick_completer ($2));
}
| primary TICK_FIRST tick_arglist | primary TICK_FIRST tick_arglist
{ {
operation_up arg = ada_pop (); operation_up arg = ada_pop ();

View file

@ -39,6 +39,11 @@ OPER ([-+*/=<>&]|"<="|">="|"**"|"/="|"and"|"or"|"xor"|"not"|"mod"|"rem"|"abs"
EXP (e[+-]{NUM10}) EXP (e[+-]{NUM10})
POSEXP (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" #include "diagnostics.h"
@ -73,16 +78,35 @@ static void rewind_to_char (int);
Defining YY_NO_INPUT comments it out. */ Defining YY_NO_INPUT comments it out. */
#define YY_NO_INPUT #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 #undef YY_INPUT
#define YY_INPUT(BUF, RESULT, MAX_SIZE) \ #define YY_INPUT(BUF, RESULT, MAX_SIZE) \
if ( *pstate->lexptr == '\000' ) \ if ( *pstate->lexptr == '\000' ) \
(RESULT) = YY_NULL; \ { \
else \ if (pstate->parse_completion && !returned_complete) \
{ \ { \
*(BUF) = *pstate->lexptr; \ returned_complete = true; \
(RESULT) = 1; \ *(BUF) = COMPLETE_CHAR; \
pstate->lexptr += 1; \ (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 *); static int find_dot_all (const char *);
@ -227,7 +251,7 @@ false { return FALSEKEYWORD; }
/* ATTRIBUTES */ /* ATTRIBUTES */
{TICK}[a-z][a-z_]+ { BEGIN INITIAL; return processAttribute (yytext); } {TICK}([a-z][a-z_]*)?{COMPLETE}? { BEGIN INITIAL; return processAttribute (yytext); }
/* PUNCTUATION */ /* PUNCTUATION */
@ -239,7 +263,7 @@ false { return FALSEKEYWORD; }
"<=" { return LEQ; } "<=" { return LEQ; }
">=" { return GEQ; } ">=" { return GEQ; }
<BEFORE_QUAL_QUOTE>"'" { BEGIN INITIAL; return '\''; } <BEFORE_QUAL_QUOTE>"'"/{NOT_COMPLETE} { BEGIN INITIAL; return '\''; }
[-&*+./:<>=|;\[\]] { return yytext[0]; } [-&*+./:<>=|;\[\]] { return yytext[0]; }
@ -320,6 +344,7 @@ lexer_init (FILE *inp)
{ {
BEGIN INITIAL; BEGIN INITIAL;
paren_depth = 0; paren_depth = 0;
returned_complete = false;
yyrestart (inp); yyrestart (inp);
} }
@ -668,6 +693,16 @@ processAttribute (const char *str)
while (isspace (*str)) while (isspace (*str))
++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) for (const auto &item : attributes)
if (strcasecmp (str, item.name) == 0) if (strcasecmp (str, item.name) == 0)
return item.code; return item.code;
@ -687,6 +722,20 @@ processAttribute (const char *str)
return *found; 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 /* Back up lexptr by yyleng and then to the rightmost occurrence of
character CH, case-folded (there must be one). WARNING: since character CH, case-folded (there must be one). WARNING: since
lexptr points to the next input character that Flex has not yet lexptr points to the next input character that Flex has not yet

View file

@ -195,6 +195,14 @@ struct parser_state : public expr_builder
void mark_completion_tag (enum type_code tag, const char *ptr, int length); 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. */ /* Push an operation on the stack. */
void push (expr::operation_up &&op) void push (expr::operation_up &&op)
{ {

View file

@ -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 return 0
} }