-/* FLEX lexer for Ada expressions, for GDB.
- Copyright (C) 1994-2019 Free Software Foundation, Inc.
+/* FLEX lexer for Ada expressions, for GDB. -*- c++ -*-
+ Copyright (C) 1994-2022 Free Software Foundation, Inc.
This file is part of GDB.
NUM16 ({HEXDIG}({HEXDIG}|_)*)
OCTDIG [0-7]
LETTER [a-z_]
-ID ({LETTER}({LETTER}|{DIG})*|"<"{LETTER}({LETTER}|{DIG})*">")
+ID ({LETTER}({LETTER}|{DIG}|[\x80-\xff])*|"<"{LETTER}({LETTER}|{DIG})*">")
WHITE [ \t\n]
TICK ("'"{WHITE}*)
GRAPHIC [a-z0-9 #&'()*+,-./:;<>=_|!$%?@\[\]\\^`{}~]
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"
/* Some old versions of flex generate code that uses the "register" keyword,
which clang warns about. This was observed for example with flex 2.5.35,
- as shipped with macOS 10.12. */
+ as shipped with macOS 10.12. The same happens with flex 2.5.37 and g++ 11
+ which defaults to ISO C++17, that does not allow register storage class
+ specifiers. */
DIAGNOSTIC_PUSH
DIAGNOSTIC_IGNORE_DEPRECATED_REGISTER
Defining YY_NO_INPUT comments it out. */
#define YY_NO_INPUT
-#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; \
- }
+/* 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;
-static int find_dot_all (const char *);
+/* 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' ) \
+ { \
+ 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; \
+ }
/* Depth of parentheses. */
static int paren_depth;
{NUM10}{POSEXP} {
canonicalizeNumeral (numbuf, yytext);
- return processInt (pstate, NULL, numbuf,
- strrchr (numbuf, 'e') + 1);
+ char *e_ptr = strrchr (numbuf, 'e');
+ *e_ptr = '\0';
+ return processInt (pstate, nullptr, numbuf, e_ptr + 1);
}
{NUM10} {
{NUM10}"#"{HEXDIG}({HEXDIG}|_)*"#"{POSEXP} {
canonicalizeNumeral (numbuf, yytext);
+ char *e_ptr = strrchr (numbuf, 'e');
+ *e_ptr = '\0';
return processInt (pstate, numbuf,
strchr (numbuf, '#') + 1,
- strrchr(numbuf, '#') + 1);
+ e_ptr + 1);
}
-{NUM10}"#"{HEXDIG}({HEXDIG}|_)*"#" {
+ /* The "llf" is a gdb extension to allow a floating-point
+ constant to be written in some other base. The
+ floating-point number is formed by reinterpreting the
+ bytes, allowing direct control over the bits. */
+{NUM10}(l{0,2}f)?"#"{HEXDIG}({HEXDIG}|_)*"#" {
canonicalizeNumeral (numbuf, yytext);
return processInt (pstate, numbuf, strchr (numbuf, '#') + 1,
NULL);
}
<INITIAL>"'"({GRAPHIC}|\")"'" {
- yylval.typed_val.type = type_char (pstate);
yylval.typed_val.val = yytext[1];
+ yylval.typed_val.type = type_for_char (pstate, yytext[1]);
return CHARLIT;
}
-<INITIAL>"'[\""{HEXDIG}{2}"\"]'" {
- int v;
- yylval.typed_val.type = type_char (pstate);
- sscanf (yytext+3, "%2x", &v);
+<INITIAL>"'[\""{HEXDIG}{2,}"\"]'" {
+ ULONGEST v = strtoulst (yytext+3, nullptr, 16);
yylval.typed_val.val = v;
+ yylval.typed_val.type = type_for_char (pstate, v);
return CHARLIT;
}
-\"({GRAPHIC}|"[\""({HEXDIG}{2}|\")"\"]")*\" {
+ /* Note that we don't handle bracket sequences of more than 2
+ digits here. Currently there's no support for wide or
+ wide-wide strings. */
+\"({GRAPHIC}|"[\""({HEXDIG}{2,}|\")"\"]")*\" {
yylval.sval = processString (yytext+1, yyleng-2);
return STRING;
}
/* ATTRIBUTES */
-{TICK}[a-zA-Z][a-zA-Z]+ { BEGIN INITIAL; return processAttribute (yytext+1); }
+{TICK}([a-z][a-z_]*)?{COMPLETE}? { BEGIN INITIAL; return processAttribute (yytext); }
/* PUNCTUATION */
"<=" { return LEQ; }
">=" { return GEQ; }
-<BEFORE_QUAL_QUOTE>"'" { BEGIN INITIAL; return '\''; }
+<BEFORE_QUAL_QUOTE>"'"/{NOT_COMPLETE} { BEGIN INITIAL; return '\''; }
-[-&*+./:<>=|;\[\]] { return yytext[0]; }
+[-&*+{}@/:<>=|;\[\]] { return yytext[0]; }
"," { if (paren_depth == 0 && pstate->comma_terminates)
{
}
}
-"."{WHITE}*all { return DOT_ALL; }
-
-"."{WHITE}*{ID} {
+"."{WHITE}*{ID}{COMPLETE}? {
yylval.sval = processId (yytext+1, yyleng-1);
+ if (yytext[yyleng - 1] == COMPLETE_CHAR)
+ return DOT_COMPLETE;
return DOT_ID;
}
-{ID}({WHITE}*"."{WHITE}*({ID}|\"{OPER}\"))*(" "*"'")? {
+"."{WHITE}*{COMPLETE} {
+ yylval.sval.ptr = "";
+ yylval.sval.length = 0;
+ return DOT_COMPLETE;
+ }
+
+{ID}({WHITE}*"."{WHITE}*({ID}|\"{OPER}\"))*(" "*"'"|{COMPLETE})? {
int all_posn = find_dot_all (yytext);
if (all_posn == -1 && yytext[yyleng-1] == '\'')
}
else if (all_posn >= 0)
yyless (all_posn);
+ bool is_completion = yytext[yyleng - 1] == COMPLETE_CHAR;
yylval.sval = processId (yytext, yyleng);
- return NAME;
+ return is_completion ? NAME_COMPLETE : NAME;
}
"::" { return COLONCOLON; }
-[{}@] { return yytext[0]; }
-
/* REGISTERS AND GDB CONVENIENCE VARIABLES */
"$"({LETTER}|{DIG}|"$")* {
{
BEGIN INITIAL;
paren_depth = 0;
+ returned_complete = false;
yyrestart (inp);
}
processInt (struct parser_state *par_state, const char *base0,
const char *num0, const char *exp0)
{
- ULONGEST result;
long exp;
int base;
- const char *trailer;
+ /* For the based literal with an "f" prefix, we'll return a
+ floating-point number. This counts the the number of "l"s seen,
+ to decide the width of the floating-point number to return. -1
+ means no "f". */
+ int floating_point_l_count = -1;
if (base0 == NULL)
base = 10;
else
{
- base = strtol (base0, (char **) NULL, 10);
+ char *end_of_base;
+ base = strtol (base0, &end_of_base, 10);
if (base < 2 || base > 16)
error (_("Invalid base: %d."), base);
+ while (*end_of_base == 'l')
+ {
+ ++floating_point_l_count;
+ ++end_of_base;
+ }
+ /* This assertion is ensured by the pattern. */
+ gdb_assert (floating_point_l_count == -1 || *end_of_base == 'f');
+ if (*end_of_base == 'f')
+ {
+ ++end_of_base;
+ ++floating_point_l_count;
+ }
+ /* This assertion is ensured by the pattern. */
+ gdb_assert (*end_of_base == '#');
}
if (exp0 == NULL)
else
exp = strtol(exp0, (char **) NULL, 10);
- errno = 0;
- result = strtoulst (num0, &trailer, base);
- if (errno == ERANGE)
- error (_("Integer literal out of range"));
- if (isxdigit(*trailer))
- error (_("Invalid digit `%c' in based literal"), *trailer);
+ gdb_mpz result;
+ while (isxdigit (*num0))
+ {
+ int dig = fromhex (*num0);
+ if (dig >= base)
+ error (_("Invalid digit `%c' in based literal"), *num0);
+ mpz_mul_ui (result.val, result.val, base);
+ mpz_add_ui (result.val, result.val, dig);
+ ++num0;
+ }
while (exp > 0)
{
- if (result > (ULONG_MAX / base))
- error (_("Integer literal out of range"));
- result *= base;
+ mpz_mul_ui (result.val, result.val, base);
exp -= 1;
}
- if ((result >> (gdbarch_int_bit (par_state->gdbarch ())-1)) == 0)
+ if (floating_point_l_count > -1)
+ {
+ struct type *fp_type;
+ if (floating_point_l_count == 0)
+ fp_type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "float");
+ else if (floating_point_l_count == 1)
+ fp_type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "long_float");
+ else
+ {
+ /* This assertion is ensured by the pattern. */
+ gdb_assert (floating_point_l_count == 2);
+ fp_type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "long_long_float");
+ }
+
+ yylval.typed_val_float.type = fp_type;
+ result.write (gdb::make_array_view (yylval.typed_val_float.val,
+ TYPE_LENGTH (fp_type)),
+ type_byte_order (fp_type),
+ true);
+
+ return FLOAT;
+ }
+
+ gdb_mpz maxval (ULONGEST_MAX);
+ if (mpz_cmp (result.val, maxval.val) > 0)
+ error (_("Integer literal out of range"));
+
+ ULONGEST value = result.as_integer<ULONGEST> ();
+ if ((value >> (gdbarch_int_bit (par_state->gdbarch ())-1)) == 0)
yylval.typed_val.type = type_int (par_state);
- else if ((result >> (gdbarch_long_bit (par_state->gdbarch ())-1)) == 0)
+ else if ((value >> (gdbarch_long_bit (par_state->gdbarch ())-1)) == 0)
yylval.typed_val.type = type_long (par_state);
- else if (((result >> (gdbarch_long_bit (par_state->gdbarch ())-1)) >> 1) == 0)
+ else if (((value >> (gdbarch_long_bit (par_state->gdbarch ())-1)) >> 1) == 0)
{
/* We have a number representable as an unsigned integer quantity.
For consistency with the C treatment, we will treat it as an
*/
yylval.typed_val.type
= builtin_type (par_state->gdbarch ())->builtin_unsigned_long;
- if (result & LONGEST_SIGN)
+ if (value & LONGEST_SIGN)
yylval.typed_val.val =
- (LONGEST) (result & ~LONGEST_SIGN)
+ (LONGEST) (value & ~LONGEST_SIGN)
- (LONGEST_SIGN>>1) - (LONGEST_SIGN>>1);
else
- yylval.typed_val.val = (LONGEST) result;
+ yylval.typed_val.val = (LONGEST) value;
return INT;
}
else
yylval.typed_val.type = type_long_long (par_state);
- yylval.typed_val.val = (LONGEST) result;
+ yylval.typed_val.val = value;
return INT;
}
return result;
}
+ bool in_quotes = false;
i = i0 = 0;
while (i0 < len)
{
- if (isalnum (name0[i0]))
+ if (name0[i0] == COMPLETE_CHAR)
+ {
+ /* Just ignore. */
+ ++i0;
+ }
+ else if (in_quotes)
+ name[i++] = name0[i0++];
+ else if (isalnum (name0[i0]))
{
name[i] = tolower (name0[i0]);
i += 1; i0 += 1;
}
- else switch (name0[i0])
+ else if (isspace (name0[i0]))
+ i0 += 1;
+ else if (name0[i0] == '\'')
{
- default:
- name[i] = name0[i0];
- i += 1; i0 += 1;
- break;
- case ' ': case '\t':
- i0 += 1;
- break;
- case '\'':
- do
- {
- name[i] = name0[i0];
- i += 1; i0 += 1;
- }
- while (i0 < len && name0[i0] != '\'');
- i0 += 1;
- break;
+ /* Copy the starting quote, but not the ending quote. */
+ if (!in_quotes)
+ name[i++] = name0[i0++];
+ in_quotes = !in_quotes;
}
+ else
+ name[i++] = name0[i0++];
}
name[i] = '\000';
}
else
{
- int chr;
- sscanf (p+2, "%2x", &chr);
+ const char *end;
+ ULONGEST chr = strtoulst (p + 2, &end, 16);
+ if (chr > 0xff)
+ error (_("wide strings are not yet supported"));
*q = (char) chr;
- p += 5;
+ p = end + 1;
}
}
else
{ "size", TICK_SIZE },
{ "tag", TICK_TAG },
{ "val", TICK_VAL },
- { NULL, -1 }
};
/* Return the syntactic code corresponding to the attribute name or
static int
processAttribute (const char *str)
{
- int i, k;
+ gdb_assert (*str == '\'');
+ ++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 (i = 0; attributes[i].code != -1; i += 1)
- if (strcasecmp (str, attributes[i].name) == 0)
- return attributes[i].code;
+ for (const auto &item : attributes)
+ if (strcasecmp (str, item.name) == 0)
+ return item.code;
- for (i = 0, k = -1; attributes[i].code != -1; i += 1)
- if (subseqMatch (str, attributes[i].name))
+ gdb::optional<int> found;
+ for (const auto &item : attributes)
+ if (subseqMatch (str, item.name))
{
- if (k == -1)
- k = i;
+ if (!found.has_value ())
+ found = item.code;
else
error (_("ambiguous attribute name: `%s'"), str);
}
- if (k == -1)
+ if (!found.has_value ())
error (_("unrecognized attribute: `%s'"), str);
- return attributes[k].code;
+ 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