Rewrite the Rust expression parser
authorTom Tromey <tom@tromey.com>
Fri, 16 Apr 2021 22:34:07 +0000 (16:34 -0600)
committerTom Tromey <tom@tromey.com>
Fri, 16 Apr 2021 22:34:08 +0000 (16:34 -0600)
The Rust expression parser was written to construct its own AST, then
lower this to GDB expressions.  I did this primarily because the old
expressions were difficult to work with; after rewriting those, I
realized I could remove the AST from the Rust parser.

After looking at this, I realized it might be simpler to rewrite the
parser.  This patch reimplements it as a recursive-descent parser.  I
kept a fair amount of the existing code -- the lexer is pulled in
nearly unchanged.

There are several benefits to this approach:

* The parser is shorter now (from 2882 LOC to 2351).
* The parser is just ordinary C++ code that can be debugged in the
  usual way.
* Memory management in the parser is now straightforward, as
  parsing methods simply return a unique pointer or vector.

This required a couple of minor changes to the test suite, as some
errors have changed.

While this passes the tests, it's possible there are lurking bugs,
particularly around error handling.

gdb/ChangeLog
2021-04-16  Tom Tromey  <tom@tromey.com>

* rust-parse.c: New file.
* rust-exp.y: Remove.
* Makefile.in (COMMON_SFILES): Add rust-parse.c.
(SFILES): Remove rust-exp.y.
(YYFILES, local-maintainer-clean): Remove rust-exp.c.

gdb/testsuite/ChangeLog
2021-04-16  Tom Tromey  <tom@tromey.com>

* gdb.rust/simple.exp: Change error text.
* gdb.rust/expr.exp: Change error text.

gdb/ChangeLog
gdb/Makefile.in
gdb/rust-exp.y [deleted file]
gdb/rust-parse.c [new file with mode: 0644]
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.rust/expr.exp
gdb/testsuite/gdb.rust/simple.exp

index c8630a43014e2ac59acaa753d450e845bbc88df2..d5b6c30908453c9d515f0cfb6baf8c83f8186f48 100644 (file)
@@ -1,3 +1,11 @@
+2021-04-16  Tom Tromey  <tom@tromey.com>
+
+       * rust-parse.c: New file.
+       * rust-exp.y: Remove.
+       * Makefile.in (COMMON_SFILES): Add rust-parse.c.
+       (SFILES): Remove rust-exp.y.
+       (YYFILES, local-maintainer-clean): Remove rust-exp.c.
+
 2021-04-16  Luis Machado  <luis.machado@linaro.org>
 
        * arch-utils.c (default_floatformat_for_type): Handle bfloat16.
index 3318c1a52158ca5082b4d21ad80343544c7cb576..490419030a32085f8d316eb70388546474fc78fb 100644 (file)
@@ -1134,6 +1134,7 @@ COMMON_SFILES = \
        reverse.c \
        run-on-main-thread.c \
        rust-lang.c \
+       rust-parse.c \
        sentinel-frame.c \
        ser-event.c \
        serial.c \
@@ -1200,7 +1201,6 @@ SFILES = \
        m2-exp.y \
        p-exp.y \
        proc-service.list \
-       rust-exp.y \
        ser-base.c \
        ser-unix.c \
        sol-thread.c \
@@ -1608,8 +1608,7 @@ YYFILES = \
        f-exp.c \
        go-exp.c \
        m2-exp.c \
-       p-exp.c \
-       rust-exp.c
+       p-exp.c
 
 # ada-lex.c is included by another file, so it shouldn't wind up as a
 # .o itself.
@@ -1969,7 +1968,7 @@ local-maintainer-clean:
        rm -f c-exp.c \
                cp-name-parser.c \
                ada-lex.c ada-exp.c \
-               d-exp.c f-exp.c go-exp.c m2-exp.c p-exp.c rust-exp.c
+               d-exp.c f-exp.c go-exp.c m2-exp.c p-exp.c
        rm -f TAGS
        rm -f $(YYFILES)
        rm -f nm.h config.status
diff --git a/gdb/rust-exp.y b/gdb/rust-exp.y
deleted file mode 100644 (file)
index a0ceb6a..0000000
+++ /dev/null
@@ -1,2884 +0,0 @@
-/* Bison parser for Rust expressions, for GDB.
-   Copyright (C) 2016-2021 Free Software Foundation, Inc.
-
-   This file is part of GDB.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
-
-/* The Bison manual says that %pure-parser is deprecated, but we use
-   it anyway because it also works with Byacc.  That is also why
-   this uses %lex-param and %parse-param rather than the simpler
-   %param -- Byacc does not support the latter.  */
-%pure-parser
-%lex-param {struct rust_parser *parser}
-%parse-param {struct rust_parser *parser}
-
-/* Removing the last conflict seems difficult.  */
-%expect 1
-
-%{
-
-#include "defs.h"
-
-#include "block.h"
-#include "charset.h"
-#include "cp-support.h"
-#include "gdb_obstack.h"
-#include "gdb_regex.h"
-#include "rust-lang.h"
-#include "parser-defs.h"
-#include "gdbsupport/selftest.h"
-#include "value.h"
-#include "gdbarch.h"
-#include "rust-exp.h"
-#include <unordered_map>
-#include "gdbsupport/hash_enum.h"
-
-#define GDB_YY_REMAP_PREFIX rust
-#include "yy-remap.h"
-
-#define RUSTSTYPE YYSTYPE
-
-struct rust_op;
-typedef std::vector<const struct rust_op *> rust_op_vector;
-
-/* A typed integer constant.  */
-
-struct typed_val_int
-{
-  LONGEST val;
-  struct type *type;
-};
-
-/* A typed floating point constant.  */
-
-struct typed_val_float
-{
-  gdb_byte val[16];
-  struct type *type;
-};
-
-/* An identifier and an expression.  This is used to represent one
-   element of a struct initializer.  */
-
-struct set_field
-{
-  struct stoken name;
-  const struct rust_op *init;
-};
-
-typedef std::vector<set_field> rust_set_vector;
-
-%}
-
-%union
-{
-  /* A typed integer constant.  */
-  struct typed_val_int typed_val_int;
-
-  /* A typed floating point constant.  */
-  struct typed_val_float typed_val_float;
-
-  /* An identifier or string.  */
-  struct stoken sval;
-
-  /* A token representing an opcode, like "==".  */
-  enum exp_opcode opcode;
-
-  /* A list of expressions; for example, the arguments to a function
-     call.  */
-  rust_op_vector *params;
-
-  /* A list of field initializers.  */
-  rust_set_vector *field_inits;
-
-  /* A single field initializer.  */
-  struct set_field one_field_init;
-
-  /* An expression.  */
-  const struct rust_op *op;
-
-  /* A plain integer, for example used to count the number of
-     "super::" prefixes on a path.  */
-  unsigned int depth;
-}
-
-%{
-
-struct rust_parser;
-static int rustyylex (YYSTYPE *, rust_parser *);
-static void rustyyerror (rust_parser *parser, const char *msg);
-
-static struct stoken make_stoken (const char *);
-
-/* A regular expression for matching Rust numbers.  This is split up
-   since it is very long and this gives us a way to comment the
-   sections.  */
-
-static const char number_regex_text[] =
-  /* subexpression 1: allows use of alternation, otherwise uninteresting */
-  "^("
-  /* First comes floating point.  */
-  /* Recognize number after the decimal point, with optional
-     exponent and optional type suffix.
-     subexpression 2: allows "?", otherwise uninteresting
-     subexpression 3: if present, type suffix
-  */
-  "[0-9][0-9_]*\\.[0-9][0-9_]*([eE][-+]?[0-9][0-9_]*)?(f32|f64)?"
-#define FLOAT_TYPE1 3
-  "|"
-  /* Recognize exponent without decimal point, with optional type
-     suffix.
-     subexpression 4: if present, type suffix
-  */
-#define FLOAT_TYPE2 4
-  "[0-9][0-9_]*[eE][-+]?[0-9][0-9_]*(f32|f64)?"
-  "|"
-  /* "23." is a valid floating point number, but "23.e5" and
-     "23.f32" are not.  So, handle the trailing-. case
-     separately.  */
-  "[0-9][0-9_]*\\."
-  "|"
-  /* Finally come integers.
-     subexpression 5: text of integer
-     subexpression 6: if present, type suffix
-     subexpression 7: allows use of alternation, otherwise uninteresting
-  */
-#define INT_TEXT 5
-#define INT_TYPE 6
-  "(0x[a-fA-F0-9_]+|0o[0-7_]+|0b[01_]+|[0-9][0-9_]*)"
-  "([iu](size|8|16|32|64))?"
-  ")";
-/* The number of subexpressions to allocate space for, including the
-   "0th" whole match subexpression.  */
-#define NUM_SUBEXPRESSIONS 8
-
-/* The compiled number-matching regex.  */
-
-static regex_t number_regex;
-
-/* An instance of this is created before parsing, and destroyed when
-   parsing is finished.  */
-
-struct rust_parser
-{
-  rust_parser (struct parser_state *state)
-    : rust_ast (nullptr),
-      pstate (state)
-  {
-  }
-
-  ~rust_parser ()
-  {
-  }
-
-  /* Create a new rust_set_vector.  The storage for the new vector is
-     managed by this class.  */
-  rust_set_vector *new_set_vector ()
-  {
-    rust_set_vector *result = new rust_set_vector;
-    set_vectors.push_back (std::unique_ptr<rust_set_vector> (result));
-    return result;
-  }
-
-  /* Create a new rust_ops_vector.  The storage for the new vector is
-     managed by this class.  */
-  rust_op_vector *new_op_vector ()
-  {
-    rust_op_vector *result = new rust_op_vector;
-    op_vectors.push_back (std::unique_ptr<rust_op_vector> (result));
-    return result;
-  }
-
-  /* Return the parser's language.  */
-  const struct language_defn *language () const
-  {
-    return pstate->language ();
-  }
-
-  /* Return the parser's gdbarch.  */
-  struct gdbarch *arch () const
-  {
-    return pstate->gdbarch ();
-  }
-
-  /* A helper to look up a Rust type, or fail.  This only works for
-     types defined by rust_language_arch_info.  */
-
-  struct type *get_type (const char *name)
-  {
-    struct type *type;
-
-    type = language_lookup_primitive_type (language (), arch (), name);
-    if (type == NULL)
-      error (_("Could not find Rust type %s"), name);
-    return type;
-  }
-
-  const char *copy_name (const char *name, int len);
-  struct stoken concat3 (const char *s1, const char *s2, const char *s3);
-  const struct rust_op *crate_name (const struct rust_op *name);
-  const struct rust_op *super_name (const struct rust_op *ident,
-                                   unsigned int n_supers);
-
-  int lex_character (YYSTYPE *lvalp);
-  int lex_number (YYSTYPE *lvalp);
-  int lex_string (YYSTYPE *lvalp);
-  int lex_identifier (YYSTYPE *lvalp);
-  uint32_t lex_hex (int min, int max);
-  uint32_t lex_escape (int is_byte);
-  int lex_operator (YYSTYPE *lvalp);
-  void push_back (char c);
-
-  void update_innermost_block (struct block_symbol sym);
-  struct block_symbol lookup_symbol (const char *name,
-                                    const struct block *block,
-                                    const domain_enum domain);
-  struct type *rust_lookup_type (const char *name, const struct block *block);
-  std::vector<struct type *> convert_params_to_types (rust_op_vector *params);
-  struct type *convert_ast_to_type (const struct rust_op *operation);
-  const char *convert_name (const struct rust_op *operation);
-  std::vector<expr::operation_up> convert_params_to_expression
-       (rust_op_vector *params, const struct rust_op *top);
-  expr::operation_up convert_ast_to_expression (const struct rust_op *opn,
-                                               const struct rust_op *top,
-                                               bool want_type = false);
-
-  struct rust_op *ast_basic_type (enum type_code typecode);
-  const struct rust_op *ast_operation (enum exp_opcode opcode,
-                                      const struct rust_op *left,
-                                      const struct rust_op *right);
-  const struct rust_op *ast_compound_assignment
-  (enum exp_opcode opcode, const struct rust_op *left,
-   const struct rust_op *rust_op);
-  const struct rust_op *ast_literal (struct typed_val_int val);
-  const struct rust_op *ast_dliteral (struct typed_val_float val);
-  const struct rust_op *ast_structop (const struct rust_op *left,
-                                     const char *name,
-                                     int completing);
-  const struct rust_op *ast_structop_anonymous
-  (const struct rust_op *left, struct typed_val_int number);
-  const struct rust_op *ast_unary (enum exp_opcode opcode,
-                                  const struct rust_op *expr);
-  const struct rust_op *ast_cast (const struct rust_op *expr,
-                                 const struct rust_op *type);
-  const struct rust_op *ast_call_ish (enum exp_opcode opcode,
-                                     const struct rust_op *expr,
-                                     rust_op_vector *params);
-  const struct rust_op *ast_path (struct stoken name,
-                                 rust_op_vector *params);
-  const struct rust_op *ast_string (struct stoken str);
-  const struct rust_op *ast_struct (const struct rust_op *name,
-                                   rust_set_vector *fields);
-  const struct rust_op *ast_range (const struct rust_op *lhs,
-                                  const struct rust_op *rhs,
-                                  bool inclusive);
-  const struct rust_op *ast_array_type (const struct rust_op *lhs,
-                                       struct typed_val_int val);
-  const struct rust_op *ast_slice_type (const struct rust_op *type);
-  const struct rust_op *ast_reference_type (const struct rust_op *type);
-  const struct rust_op *ast_pointer_type (const struct rust_op *type,
-                                         int is_mut);
-  const struct rust_op *ast_function_type (const struct rust_op *result,
-                                          rust_op_vector *params);
-  const struct rust_op *ast_tuple_type (rust_op_vector *params);
-
-
-  /* A pointer to this is installed globally.  */
-  auto_obstack obstack;
-
-  /* Result of parsing.  Points into obstack.  */
-  const struct rust_op *rust_ast;
-
-  /* This keeps track of the various vectors we allocate.  */
-  std::vector<std::unique_ptr<rust_set_vector>> set_vectors;
-  std::vector<std::unique_ptr<rust_op_vector>> op_vectors;
-
-  /* The parser state gdb gave us.  */
-  struct parser_state *pstate;
-
-  /* Depth of parentheses.  */
-  int paren_depth = 0;
-};
-
-/* Rust AST operations.  We build a tree of these; then lower them to
-   gdb expressions when parsing has completed.  */
-
-struct rust_op
-{
-  /* The opcode.  */
-  enum exp_opcode opcode;
-  /* If OPCODE is OP_TYPE, then this holds information about what type
-     is described by this node.  */
-  enum type_code typecode;
-  /* Indicates whether OPCODE actually represents a compound
-     assignment.  For example, if OPCODE is GTGT and this is false,
-     then this rust_op represents an ordinary ">>"; but if this is
-     true, then this rust_op represents ">>=".  Unused in other
-     cases.  */
-  unsigned int compound_assignment : 1;
-  /* Only used by a field expression; if set, indicates that the field
-     name occurred at the end of the expression and is eligible for
-     completion.  */
-  unsigned int completing : 1;
-  /* For OP_RANGE, indicates whether the range is inclusive or
-     exclusive.  */
-  unsigned int inclusive : 1;
-  /* Operands of expression.  Which one is used and how depends on the
-     particular opcode.  */
-  RUSTSTYPE left;
-  RUSTSTYPE right;
-};
-
-%}
-
-%token <sval> GDBVAR
-%token <sval> IDENT
-%token <sval> COMPLETE
-%token <typed_val_int> INTEGER
-%token <typed_val_int> DECIMAL_INTEGER
-%token <sval> STRING
-%token <sval> BYTESTRING
-%token <typed_val_float> FLOAT
-%token <opcode> COMPOUND_ASSIGN
-
-/* Keyword tokens.  */
-%token <voidval> KW_AS
-%token <voidval> KW_IF
-%token <voidval> KW_TRUE
-%token <voidval> KW_FALSE
-%token <voidval> KW_SUPER
-%token <voidval> KW_SELF
-%token <voidval> KW_MUT
-%token <voidval> KW_EXTERN
-%token <voidval> KW_CONST
-%token <voidval> KW_FN
-%token <voidval> KW_SIZEOF
-
-/* Operator tokens.  */
-%token <voidval> DOTDOT
-%token <voidval> DOTDOTEQ
-%token <voidval> OROR
-%token <voidval> ANDAND
-%token <voidval> EQEQ
-%token <voidval> NOTEQ
-%token <voidval> LTEQ
-%token <voidval> GTEQ
-%token <voidval> LSH RSH
-%token <voidval> COLONCOLON
-%token <voidval> ARROW
-
-%type <op> type
-%type <op> path_for_expr
-%type <op> identifier_path_for_expr
-%type <op> path_for_type
-%type <op> identifier_path_for_type
-%type <op> just_identifiers_for_type
-
-%type <params> maybe_type_list
-%type <params> type_list
-
-%type <depth> super_path
-
-%type <op> literal
-%type <op> expr
-%type <op> field_expr
-%type <op> idx_expr
-%type <op> unop_expr
-%type <op> binop_expr
-%type <op> binop_expr_expr
-%type <op> type_cast_expr
-%type <op> assignment_expr
-%type <op> compound_assignment_expr
-%type <op> paren_expr
-%type <op> call_expr
-%type <op> path_expr
-%type <op> tuple_expr
-%type <op> unit_expr
-%type <op> struct_expr
-%type <op> array_expr
-%type <op> range_expr
-
-%type <params> expr_list
-%type <params> maybe_expr_list
-%type <params> paren_expr_list
-
-%type <field_inits> struct_expr_list
-%type <one_field_init> struct_expr_tail
-
-/* Precedence.  */
-%nonassoc DOTDOT DOTDOTEQ
-%right '=' COMPOUND_ASSIGN
-%left OROR
-%left ANDAND
-%nonassoc EQEQ NOTEQ '<' '>' LTEQ GTEQ
-%left '|'
-%left '^'
-%left '&'
-%left LSH RSH
-%left '@'
-%left '+' '-'
-%left '*' '/' '%'
-/* These could be %precedence in Bison, but that isn't a yacc
-   feature.  */
-%left KW_AS
-%left UNARY
-%left '[' '.' '('
-
-%%
-
-start:
-       expr
-               {
-                 /* If we are completing and see a valid parse,
-                    rust_ast will already have been set.  */
-                 if (parser->rust_ast == NULL)
-                   parser->rust_ast = $1;
-               }
-;
-
-/* Note that the Rust grammar includes a method_call_expr, but we
-   handle this differently, to avoid a shift/reduce conflict with
-   call_expr.  */
-expr:
-       literal
-|      path_expr
-|      tuple_expr
-|      unit_expr
-|      struct_expr
-|      field_expr
-|      array_expr
-|      idx_expr
-|      range_expr
-|      unop_expr /* Must precede call_expr because of ambiguity with
-                    sizeof.  */
-|      binop_expr
-|      paren_expr
-|      call_expr
-;
-
-tuple_expr:
-       '(' expr ',' maybe_expr_list ')'
-               {
-                 $4->push_back ($2);
-                 error (_("Tuple expressions not supported yet"));
-               }
-;
-
-unit_expr:
-       '(' ')'
-               {
-                 struct typed_val_int val;
-
-                 val.type
-                   = (language_lookup_primitive_type
-                      (parser->language (), parser->arch (),
-                       "()"));
-                 val.val = 0;
-                 $$ = parser->ast_literal (val);
-               }
-;
-
-/* To avoid a shift/reduce conflict with call_expr, we don't handle
-   tuple struct expressions here, but instead when examining the
-   AST.  */
-struct_expr:
-       path_for_expr '{' struct_expr_list '}'
-               { $$ = parser->ast_struct ($1, $3); }
-;
-
-struct_expr_tail:
-       DOTDOT expr
-               {
-                 struct set_field sf;
-
-                 sf.name.ptr = NULL;
-                 sf.name.length = 0;
-                 sf.init = $2;
-
-                 $$ = sf;
-               }
-|      IDENT ':' expr
-               {
-                 struct set_field sf;
-
-                 sf.name = $1;
-                 sf.init = $3;
-                 $$ = sf;
-               }
-|      IDENT
-               {
-                 struct set_field sf;
-
-                 sf.name = $1;
-                 sf.init = parser->ast_path ($1, NULL);
-                 $$ = sf;
-               }
-;
-
-struct_expr_list:
-       /* %empty */
-               {
-                 $$ = parser->new_set_vector ();
-               }
-|      struct_expr_tail
-               {
-                 rust_set_vector *result = parser->new_set_vector ();
-                 result->push_back ($1);
-                 $$ = result;
-               }
-|      IDENT ':' expr ',' struct_expr_list
-               {
-                 struct set_field sf;
-
-                 sf.name = $1;
-                 sf.init = $3;
-                 $5->push_back (sf);
-                 $$ = $5;
-               }
-|      IDENT ',' struct_expr_list
-               {
-                 struct set_field sf;
-
-                 sf.name = $1;
-                 sf.init = parser->ast_path ($1, NULL);
-                 $3->push_back (sf);
-                 $$ = $3;
-               }
-;
-
-array_expr:
-       '[' KW_MUT expr_list ']'
-               { $$ = parser->ast_call_ish (OP_ARRAY, NULL, $3); }
-|      '[' expr_list ']'
-               { $$ = parser->ast_call_ish (OP_ARRAY, NULL, $2); }
-|      '[' KW_MUT expr ';' expr ']'
-               { $$ = parser->ast_operation (OP_RUST_ARRAY, $3, $5); }
-|      '[' expr ';' expr ']'
-               { $$ = parser->ast_operation (OP_RUST_ARRAY, $2, $4); }
-;
-
-range_expr:
-       expr DOTDOT
-               { $$ = parser->ast_range ($1, NULL, false); }
-|      expr DOTDOT expr
-               { $$ = parser->ast_range ($1, $3, false); }
-|      expr DOTDOTEQ expr
-               { $$ = parser->ast_range ($1, $3, true); }
-|      DOTDOT expr
-               { $$ = parser->ast_range (NULL, $2, false); }
-|      DOTDOTEQ expr
-               { $$ = parser->ast_range (NULL, $2, true); }
-|      DOTDOT
-               { $$ = parser->ast_range (NULL, NULL, false); }
-;
-
-literal:
-       INTEGER
-               { $$ = parser->ast_literal ($1); }
-|      DECIMAL_INTEGER
-               { $$ = parser->ast_literal ($1); }
-|      FLOAT
-               { $$ = parser->ast_dliteral ($1); }
-|      STRING
-               {
-                 struct set_field field;
-                 struct typed_val_int val;
-                 struct stoken token;
-
-                 rust_set_vector *fields = parser->new_set_vector ();
-
-                 /* Wrap the raw string in the &str struct.  */
-                 field.name.ptr = "data_ptr";
-                 field.name.length = strlen (field.name.ptr);
-                 field.init = parser->ast_unary (UNOP_ADDR,
-                                                 parser->ast_string ($1));
-                 fields->push_back (field);
-
-                 val.type = parser->get_type ("usize");
-                 val.val = $1.length;
-
-                 field.name.ptr = "length";
-                 field.name.length = strlen (field.name.ptr);
-                 field.init = parser->ast_literal (val);
-                 fields->push_back (field);
-
-                 token.ptr = "&str";
-                 token.length = strlen (token.ptr);
-                 $$ = parser->ast_struct (parser->ast_path (token, NULL),
-                                          fields);
-               }
-|      BYTESTRING
-               { $$ = parser->ast_string ($1); }
-|      KW_TRUE
-               {
-                 struct typed_val_int val;
-
-                 val.type = language_bool_type (parser->language (),
-                                                parser->arch ());
-                 val.val = 1;
-                 $$ = parser->ast_literal (val);
-               }
-|      KW_FALSE
-               {
-                 struct typed_val_int val;
-
-                 val.type = language_bool_type (parser->language (),
-                                                parser->arch ());
-                 val.val = 0;
-                 $$ = parser->ast_literal (val);
-               }
-;
-
-field_expr:
-       expr '.' IDENT
-               { $$ = parser->ast_structop ($1, $3.ptr, 0); }
-|      expr '.' COMPLETE
-               {
-                 $$ = parser->ast_structop ($1, $3.ptr, 1);
-                 parser->rust_ast = $$;
-               }
-|      expr '.' DECIMAL_INTEGER
-               { $$ = parser->ast_structop_anonymous ($1, $3); }
-;
-
-idx_expr:
-       expr '[' expr ']'
-               { $$ = parser->ast_operation (BINOP_SUBSCRIPT, $1, $3); }
-;
-
-unop_expr:
-       '+' expr        %prec UNARY
-               { $$ = parser->ast_unary (UNOP_PLUS, $2); }
-
-|      '-' expr        %prec UNARY
-               { $$ = parser->ast_unary (UNOP_NEG, $2); }
-
-|      '!' expr        %prec UNARY
-               {
-                 /* Note that we provide a Rust-specific evaluator
-                    override for UNOP_COMPLEMENT, so it can do the
-                    right thing for both bool and integral
-                    values.  */
-                 $$ = parser->ast_unary (UNOP_COMPLEMENT, $2);
-               }
-
-|      '*' expr        %prec UNARY
-               { $$ = parser->ast_unary (UNOP_IND, $2); }
-
-|      '&' expr        %prec UNARY
-               { $$ = parser->ast_unary (UNOP_ADDR, $2); }
-
-|      '&' KW_MUT expr %prec UNARY
-               { $$ = parser->ast_unary (UNOP_ADDR, $3); }
-|      KW_SIZEOF '(' expr ')' %prec UNARY
-               { $$ = parser->ast_unary (UNOP_SIZEOF, $3); }
-;
-
-binop_expr:
-       binop_expr_expr
-|      type_cast_expr
-|      assignment_expr
-|      compound_assignment_expr
-;
-
-binop_expr_expr:
-       expr '*' expr
-               { $$ = parser->ast_operation (BINOP_MUL, $1, $3); }
-
-|      expr '@' expr
-               { $$ = parser->ast_operation (BINOP_REPEAT, $1, $3); }
-
-|      expr '/' expr
-               { $$ = parser->ast_operation (BINOP_DIV, $1, $3); }
-
-|      expr '%' expr
-               { $$ = parser->ast_operation (BINOP_REM, $1, $3); }
-
-|      expr '<' expr
-               { $$ = parser->ast_operation (BINOP_LESS, $1, $3); }
-
-|      expr '>' expr
-               { $$ = parser->ast_operation (BINOP_GTR, $1, $3); }
-
-|      expr '&' expr
-               { $$ = parser->ast_operation (BINOP_BITWISE_AND, $1, $3); }
-
-|      expr '|' expr
-               { $$ = parser->ast_operation (BINOP_BITWISE_IOR, $1, $3); }
-
-|      expr '^' expr
-               { $$ = parser->ast_operation (BINOP_BITWISE_XOR, $1, $3); }
-
-|      expr '+' expr
-               { $$ = parser->ast_operation (BINOP_ADD, $1, $3); }
-
-|      expr '-' expr
-               { $$ = parser->ast_operation (BINOP_SUB, $1, $3); }
-
-|      expr OROR expr
-               { $$ = parser->ast_operation (BINOP_LOGICAL_OR, $1, $3); }
-
-|      expr ANDAND expr
-               { $$ = parser->ast_operation (BINOP_LOGICAL_AND, $1, $3); }
-
-|      expr EQEQ expr
-               { $$ = parser->ast_operation (BINOP_EQUAL, $1, $3); }
-
-|      expr NOTEQ expr
-               { $$ = parser->ast_operation (BINOP_NOTEQUAL, $1, $3); }
-
-|      expr LTEQ expr
-               { $$ = parser->ast_operation (BINOP_LEQ, $1, $3); }
-
-|      expr GTEQ expr
-               { $$ = parser->ast_operation (BINOP_GEQ, $1, $3); }
-
-|      expr LSH expr
-               { $$ = parser->ast_operation (BINOP_LSH, $1, $3); }
-
-|      expr RSH expr
-               { $$ = parser->ast_operation (BINOP_RSH, $1, $3); }
-;
-
-type_cast_expr:
-       expr KW_AS type
-               { $$ = parser->ast_cast ($1, $3); }
-;
-
-assignment_expr:
-       expr '=' expr
-               { $$ = parser->ast_operation (BINOP_ASSIGN, $1, $3); }
-;
-
-compound_assignment_expr:
-       expr COMPOUND_ASSIGN expr
-               { $$ = parser->ast_compound_assignment ($2, $1, $3); }
-
-;
-
-paren_expr:
-       '(' expr ')'
-               { $$ = $2; }
-;
-
-expr_list:
-       expr
-               {
-                 $$ = parser->new_op_vector ();
-                 $$->push_back ($1);
-               }
-|      expr_list ',' expr
-               {
-                 $1->push_back ($3);
-                 $$ = $1;
-               }
-;
-
-maybe_expr_list:
-       /* %empty */
-               {
-                 /* The result can't be NULL.  */
-                 $$ = parser->new_op_vector ();
-               }
-|      expr_list
-               { $$ = $1; }
-;
-
-paren_expr_list:
-       '(' maybe_expr_list ')'
-               { $$ = $2; }
-;
-
-call_expr:
-       expr paren_expr_list
-               { $$ = parser->ast_call_ish (OP_FUNCALL, $1, $2); }
-;
-
-maybe_self_path:
-       /* %empty */
-|      KW_SELF COLONCOLON
-;
-
-super_path:
-       KW_SUPER COLONCOLON
-               { $$ = 1; }
-|      super_path KW_SUPER COLONCOLON
-               { $$ = $1 + 1; }
-;
-
-path_expr:
-       path_for_expr
-               { $$ = $1; }
-|      GDBVAR
-               { $$ = parser->ast_path ($1, NULL); }
-|      KW_SELF
-               { $$ = parser->ast_path (make_stoken ("self"), NULL); }
-;
-
-path_for_expr:
-       identifier_path_for_expr
-|      KW_SELF COLONCOLON identifier_path_for_expr
-               { $$ = parser->super_name ($3, 0); }
-|      maybe_self_path super_path identifier_path_for_expr
-               { $$ = parser->super_name ($3, $2); }
-|      COLONCOLON identifier_path_for_expr
-               { $$ = parser->crate_name ($2); }
-|      KW_EXTERN identifier_path_for_expr
-               {
-                 /* This is a gdb extension to make it possible to
-                    refer to items in other crates.  It just bypasses
-                    adding the current crate to the front of the
-                    name.  */
-                 $$ = parser->ast_path (parser->concat3 ("::",
-                                                         $2->left.sval.ptr,
-                                                         NULL),
-                                        $2->right.params);
-               }
-;
-
-identifier_path_for_expr:
-       IDENT
-               { $$ = parser->ast_path ($1, NULL); }
-|      identifier_path_for_expr COLONCOLON IDENT
-               {
-                 $$ = parser->ast_path (parser->concat3 ($1->left.sval.ptr,
-                                                         "::", $3.ptr),
-                                        NULL);
-               }
-|      identifier_path_for_expr COLONCOLON '<' type_list '>'
-               { $$ = parser->ast_path ($1->left.sval, $4); }
-|      identifier_path_for_expr COLONCOLON '<' type_list RSH
-               {
-                 $$ = parser->ast_path ($1->left.sval, $4);
-                 parser->push_back ('>');
-               }
-;
-
-path_for_type:
-       identifier_path_for_type
-|      KW_SELF COLONCOLON identifier_path_for_type
-               { $$ = parser->super_name ($3, 0); }
-|      maybe_self_path super_path identifier_path_for_type
-               { $$ = parser->super_name ($3, $2); }
-|      COLONCOLON identifier_path_for_type
-               { $$ = parser->crate_name ($2); }
-|      KW_EXTERN identifier_path_for_type
-               {
-                 /* This is a gdb extension to make it possible to
-                    refer to items in other crates.  It just bypasses
-                    adding the current crate to the front of the
-                    name.  */
-                 $$ = parser->ast_path (parser->concat3 ("::",
-                                                         $2->left.sval.ptr,
-                                                         NULL),
-                                        $2->right.params);
-               }
-;
-
-just_identifiers_for_type:
-       IDENT
-               { $$ = parser->ast_path ($1, NULL); }
-|      just_identifiers_for_type COLONCOLON IDENT
-               {
-                 $$ = parser->ast_path (parser->concat3 ($1->left.sval.ptr,
-                                                         "::", $3.ptr),
-                                        NULL);
-               }
-;
-
-identifier_path_for_type:
-       just_identifiers_for_type
-|      just_identifiers_for_type '<' type_list '>'
-               { $$ = parser->ast_path ($1->left.sval, $3); }
-|      just_identifiers_for_type '<' type_list RSH
-               {
-                 $$ = parser->ast_path ($1->left.sval, $3);
-                 parser->push_back ('>');
-               }
-;
-
-type:
-       path_for_type
-|      '[' type ';' INTEGER ']'
-               { $$ = parser->ast_array_type ($2, $4); }
-|      '[' type ';' DECIMAL_INTEGER ']'
-               { $$ = parser->ast_array_type ($2, $4); }
-|      '&' '[' type ']'
-               { $$ = parser->ast_slice_type ($3); }
-|      '&' type
-               { $$ = parser->ast_reference_type ($2); }
-|      '*' KW_MUT type
-               { $$ = parser->ast_pointer_type ($3, 1); }
-|      '*' KW_CONST type
-               { $$ = parser->ast_pointer_type ($3, 0); }
-|      KW_FN '(' maybe_type_list ')' ARROW type
-               { $$ = parser->ast_function_type ($6, $3); }
-|      '(' maybe_type_list ')'
-               { $$ = parser->ast_tuple_type ($2); }
-;
-
-maybe_type_list:
-       /* %empty */
-               { $$ = NULL; }
-|      type_list
-               { $$ = $1; }
-;
-
-type_list:
-       type
-               {
-                 rust_op_vector *result = parser->new_op_vector ();
-                 result->push_back ($1);
-                 $$ = result;
-               }
-|      type_list ',' type
-               {
-                 $1->push_back ($3);
-                 $$ = $1;
-               }
-;
-
-%%
-
-/* A struct of this type is used to describe a token.  */
-
-struct token_info
-{
-  const char *name;
-  int value;
-  enum exp_opcode opcode;
-};
-
-/* Identifier tokens.  */
-
-static const struct token_info identifier_tokens[] =
-{
-  { "as", KW_AS, OP_NULL },
-  { "false", KW_FALSE, OP_NULL },
-  { "if", 0, OP_NULL },
-  { "mut", KW_MUT, OP_NULL },
-  { "const", KW_CONST, OP_NULL },
-  { "self", KW_SELF, OP_NULL },
-  { "super", KW_SUPER, OP_NULL },
-  { "true", KW_TRUE, OP_NULL },
-  { "extern", KW_EXTERN, OP_NULL },
-  { "fn", KW_FN, OP_NULL },
-  { "sizeof", KW_SIZEOF, OP_NULL },
-};
-
-/* Operator tokens, sorted longest first.  */
-
-static const struct token_info operator_tokens[] =
-{
-  { ">>=", COMPOUND_ASSIGN, BINOP_RSH },
-  { "<<=", COMPOUND_ASSIGN, BINOP_LSH },
-
-  { "<<", LSH, OP_NULL },
-  { ">>", RSH, OP_NULL },
-  { "&&", ANDAND, OP_NULL },
-  { "||", OROR, OP_NULL },
-  { "==", EQEQ, OP_NULL },
-  { "!=", NOTEQ, OP_NULL },
-  { "<=", LTEQ, OP_NULL },
-  { ">=", GTEQ, OP_NULL },
-  { "+=", COMPOUND_ASSIGN, BINOP_ADD },
-  { "-=", COMPOUND_ASSIGN, BINOP_SUB },
-  { "*=", COMPOUND_ASSIGN, BINOP_MUL },
-  { "/=", COMPOUND_ASSIGN, BINOP_DIV },
-  { "%=", COMPOUND_ASSIGN, BINOP_REM },
-  { "&=", COMPOUND_ASSIGN, BINOP_BITWISE_AND },
-  { "|=", COMPOUND_ASSIGN, BINOP_BITWISE_IOR },
-  { "^=", COMPOUND_ASSIGN, BINOP_BITWISE_XOR },
-  { "..=", DOTDOTEQ, OP_NULL },
-
-  { "::", COLONCOLON, OP_NULL },
-  { "..", DOTDOT, OP_NULL },
-  { "->", ARROW, OP_NULL }
-};
-
-/* Helper function to copy to the name obstack.  */
-
-const char *
-rust_parser::copy_name (const char *name, int len)
-{
-  return obstack_strndup (&obstack, name, len);
-}
-
-/* Helper function to make an stoken from a C string.  */
-
-static struct stoken
-make_stoken (const char *p)
-{
-  struct stoken result;
-
-  result.ptr = p;
-  result.length = strlen (result.ptr);
-  return result;
-}
-
-/* Helper function to concatenate three strings on the name
-   obstack.  */
-
-struct stoken
-rust_parser::concat3 (const char *s1, const char *s2, const char *s3)
-{
-  return make_stoken (obconcat (&obstack, s1, s2, s3, (char *) NULL));
-}
-
-/* Return an AST node referring to NAME, but relative to the crate's
-   name.  */
-
-const struct rust_op *
-rust_parser::crate_name (const struct rust_op *name)
-{
-  std::string crate = rust_crate_for_block (pstate->expression_context_block);
-  struct stoken result;
-
-  gdb_assert (name->opcode == OP_VAR_VALUE);
-
-  if (crate.empty ())
-    error (_("Could not find crate for current location"));
-  result = make_stoken (obconcat (&obstack, "::", crate.c_str (), "::",
-                                 name->left.sval.ptr, (char *) NULL));
-
-  return ast_path (result, name->right.params);
-}
-
-/* Create an AST node referring to a "super::" qualified name.  IDENT
-   is the base name and N_SUPERS is how many "super::"s were
-   provided.  N_SUPERS can be zero.  */
-
-const struct rust_op *
-rust_parser::super_name (const struct rust_op *ident, unsigned int n_supers)
-{
-  const char *scope = block_scope (pstate->expression_context_block);
-  int offset;
-
-  gdb_assert (ident->opcode == OP_VAR_VALUE);
-
-  if (scope[0] == '\0')
-    error (_("Couldn't find namespace scope for self::"));
-
-  if (n_supers > 0)
-    {
-      int len;
-      std::vector<int> offsets;
-      unsigned int current_len;
-
-      current_len = cp_find_first_component (scope);
-      while (scope[current_len] != '\0')
-       {
-         offsets.push_back (current_len);
-         gdb_assert (scope[current_len] == ':');
-         /* The "::".  */
-         current_len += 2;
-         current_len += cp_find_first_component (scope
-                                                 + current_len);
-       }
-
-      len = offsets.size ();
-      if (n_supers >= len)
-       error (_("Too many super:: uses from '%s'"), scope);
-
-      offset = offsets[len - n_supers];
-    }
-  else
-    offset = strlen (scope);
-
-  obstack_grow (&obstack, "::", 2);
-  obstack_grow (&obstack, scope, offset);
-  obstack_grow (&obstack, "::", 2);
-  obstack_grow0 (&obstack, ident->left.sval.ptr, ident->left.sval.length);
-
-  return ast_path (make_stoken ((const char *) obstack_finish (&obstack)),
-                  ident->right.params);
-}
-
-/* A helper that updates the innermost block as appropriate.  */
-
-void
-rust_parser::update_innermost_block (struct block_symbol sym)
-{
-  if (symbol_read_needs_frame (sym.symbol))
-    pstate->block_tracker->update (sym);
-}
-
-/* Lex a hex number with at least MIN digits and at most MAX
-   digits.  */
-
-uint32_t
-rust_parser::lex_hex (int min, int max)
-{
-  uint32_t result = 0;
-  int len = 0;
-  /* We only want to stop at MAX if we're lexing a byte escape.  */
-  int check_max = min == max;
-
-  while ((check_max ? len <= max : 1)
-        && ((pstate->lexptr[0] >= 'a' && pstate->lexptr[0] <= 'f')
-            || (pstate->lexptr[0] >= 'A' && pstate->lexptr[0] <= 'F')
-            || (pstate->lexptr[0] >= '0' && pstate->lexptr[0] <= '9')))
-    {
-      result *= 16;
-      if (pstate->lexptr[0] >= 'a' && pstate->lexptr[0] <= 'f')
-       result = result + 10 + pstate->lexptr[0] - 'a';
-      else if (pstate->lexptr[0] >= 'A' && pstate->lexptr[0] <= 'F')
-       result = result + 10 + pstate->lexptr[0] - 'A';
-      else
-       result = result + pstate->lexptr[0] - '0';
-      ++pstate->lexptr;
-      ++len;
-    }
-
-  if (len < min)
-    error (_("Not enough hex digits seen"));
-  if (len > max)
-    {
-      gdb_assert (min != max);
-      error (_("Overlong hex escape"));
-    }
-
-  return result;
-}
-
-/* Lex an escape.  IS_BYTE is true if we're lexing a byte escape;
-   otherwise we're lexing a character escape.  */
-
-uint32_t
-rust_parser::lex_escape (int is_byte)
-{
-  uint32_t result;
-
-  gdb_assert (pstate->lexptr[0] == '\\');
-  ++pstate->lexptr;
-  switch (pstate->lexptr[0])
-    {
-    case 'x':
-      ++pstate->lexptr;
-      result = lex_hex (2, 2);
-      break;
-
-    case 'u':
-      if (is_byte)
-       error (_("Unicode escape in byte literal"));
-      ++pstate->lexptr;
-      if (pstate->lexptr[0] != '{')
-       error (_("Missing '{' in Unicode escape"));
-      ++pstate->lexptr;
-      result = lex_hex (1, 6);
-      /* Could do range checks here.  */
-      if (pstate->lexptr[0] != '}')
-       error (_("Missing '}' in Unicode escape"));
-      ++pstate->lexptr;
-      break;
-
-    case 'n':
-      result = '\n';
-      ++pstate->lexptr;
-      break;
-    case 'r':
-      result = '\r';
-      ++pstate->lexptr;
-      break;
-    case 't':
-      result = '\t';
-      ++pstate->lexptr;
-      break;
-    case '\\':
-      result = '\\';
-      ++pstate->lexptr;
-      break;
-    case '0':
-      result = '\0';
-      ++pstate->lexptr;
-      break;
-    case '\'':
-      result = '\'';
-      ++pstate->lexptr;
-      break;
-    case '"':
-      result = '"';
-      ++pstate->lexptr;
-      break;
-
-    default:
-      error (_("Invalid escape \\%c in literal"), pstate->lexptr[0]);
-    }
-
-  return result;
-}
-
-/* Lex a character constant.  */
-
-int
-rust_parser::lex_character (YYSTYPE *lvalp)
-{
-  int is_byte = 0;
-  uint32_t value;
-
-  if (pstate->lexptr[0] == 'b')
-    {
-      is_byte = 1;
-      ++pstate->lexptr;
-    }
-  gdb_assert (pstate->lexptr[0] == '\'');
-  ++pstate->lexptr;
-  /* This should handle UTF-8 here.  */
-  if (pstate->lexptr[0] == '\\')
-    value = lex_escape (is_byte);
-  else
-    {
-      value = pstate->lexptr[0] & 0xff;
-      ++pstate->lexptr;
-    }
-
-  if (pstate->lexptr[0] != '\'')
-    error (_("Unterminated character literal"));
-  ++pstate->lexptr;
-
-  lvalp->typed_val_int.val = value;
-  lvalp->typed_val_int.type = get_type (is_byte ? "u8" : "char");
-
-  return INTEGER;
-}
-
-/* Return the offset of the double quote if STR looks like the start
-   of a raw string, or 0 if STR does not start a raw string.  */
-
-static int
-starts_raw_string (const char *str)
-{
-  const char *save = str;
-
-  if (str[0] != 'r')
-    return 0;
-  ++str;
-  while (str[0] == '#')
-    ++str;
-  if (str[0] == '"')
-    return str - save;
-  return 0;
-}
-
-/* Return true if STR looks like the end of a raw string that had N
-   hashes at the start.  */
-
-static bool
-ends_raw_string (const char *str, int n)
-{
-  int i;
-
-  gdb_assert (str[0] == '"');
-  for (i = 0; i < n; ++i)
-    if (str[i + 1] != '#')
-      return false;
-  return true;
-}
-
-/* Lex a string constant.  */
-
-int
-rust_parser::lex_string (YYSTYPE *lvalp)
-{
-  int is_byte = pstate->lexptr[0] == 'b';
-  int raw_length;
-
-  if (is_byte)
-    ++pstate->lexptr;
-  raw_length = starts_raw_string (pstate->lexptr);
-  pstate->lexptr += raw_length;
-  gdb_assert (pstate->lexptr[0] == '"');
-  ++pstate->lexptr;
-
-  while (1)
-    {
-      uint32_t value;
-
-      if (raw_length > 0)
-       {
-         if (pstate->lexptr[0] == '"' && ends_raw_string (pstate->lexptr,
-                                                          raw_length - 1))
-           {
-             /* Exit with lexptr pointing after the final "#".  */
-             pstate->lexptr += raw_length;
-             break;
-           }
-         else if (pstate->lexptr[0] == '\0')
-           error (_("Unexpected EOF in string"));
-
-         value = pstate->lexptr[0] & 0xff;
-         if (is_byte && value > 127)
-           error (_("Non-ASCII value in raw byte string"));
-         obstack_1grow (&obstack, value);
-
-         ++pstate->lexptr;
-       }
-      else if (pstate->lexptr[0] == '"')
-       {
-         /* Make sure to skip the quote.  */
-         ++pstate->lexptr;
-         break;
-       }
-      else if (pstate->lexptr[0] == '\\')
-       {
-         value = lex_escape (is_byte);
-
-         if (is_byte)
-           obstack_1grow (&obstack, value);
-         else
-           convert_between_encodings ("UTF-32", "UTF-8", (gdb_byte *) &value,
-                                      sizeof (value), sizeof (value),
-                                      &obstack, translit_none);
-       }
-      else if (pstate->lexptr[0] == '\0')
-       error (_("Unexpected EOF in string"));
-      else
-       {
-         value = pstate->lexptr[0] & 0xff;
-         if (is_byte && value > 127)
-           error (_("Non-ASCII value in byte string"));
-         obstack_1grow (&obstack, value);
-         ++pstate->lexptr;
-       }
-    }
-
-  lvalp->sval.length = obstack_object_size (&obstack);
-  lvalp->sval.ptr = (const char *) obstack_finish (&obstack);
-  return is_byte ? BYTESTRING : STRING;
-}
-
-/* Return true if STRING starts with whitespace followed by a digit.  */
-
-static bool
-space_then_number (const char *string)
-{
-  const char *p = string;
-
-  while (p[0] == ' ' || p[0] == '\t')
-    ++p;
-  if (p == string)
-    return false;
-
-  return *p >= '0' && *p <= '9';
-}
-
-/* Return true if C can start an identifier.  */
-
-static bool
-rust_identifier_start_p (char c)
-{
-  return ((c >= 'a' && c <= 'z')
-         || (c >= 'A' && c <= 'Z')
-         || c == '_'
-         || c == '$');
-}
-
-/* Lex an identifier.  */
-
-int
-rust_parser::lex_identifier (YYSTYPE *lvalp)
-{
-  const char *start = pstate->lexptr;
-  unsigned int length;
-  const struct token_info *token;
-  int i;
-  int is_gdb_var = pstate->lexptr[0] == '$';
-
-  gdb_assert (rust_identifier_start_p (pstate->lexptr[0]));
-
-  ++pstate->lexptr;
-
-  /* For the time being this doesn't handle Unicode rules.  Non-ASCII
-     identifiers are gated anyway.  */
-  while ((pstate->lexptr[0] >= 'a' && pstate->lexptr[0] <= 'z')
-        || (pstate->lexptr[0] >= 'A' && pstate->lexptr[0] <= 'Z')
-        || pstate->lexptr[0] == '_'
-        || (is_gdb_var && pstate->lexptr[0] == '$')
-        || (pstate->lexptr[0] >= '0' && pstate->lexptr[0] <= '9'))
-    ++pstate->lexptr;
-
-
-  length = pstate->lexptr - start;
-  token = NULL;
-  for (i = 0; i < ARRAY_SIZE (identifier_tokens); ++i)
-    {
-      if (length == strlen (identifier_tokens[i].name)
-         && strncmp (identifier_tokens[i].name, start, length) == 0)
-       {
-         token = &identifier_tokens[i];
-         break;
-       }
-    }
-
-  if (token != NULL)
-    {
-      if (token->value == 0)
-       {
-         /* Leave the terminating token alone.  */
-         pstate->lexptr = start;
-         return 0;
-       }
-    }
-  else if (token == NULL
-          && (strncmp (start, "thread", length) == 0
-              || strncmp (start, "task", length) == 0)
-          && space_then_number (pstate->lexptr))
-    {
-      /* "task" or "thread" followed by a number terminates the
-        parse, per gdb rules.  */
-      pstate->lexptr = start;
-      return 0;
-    }
-
-  if (token == NULL || (pstate->parse_completion && pstate->lexptr[0] == '\0'))
-    lvalp->sval = make_stoken (copy_name (start, length));
-
-  if (pstate->parse_completion && pstate->lexptr[0] == '\0')
-    {
-      /* Prevent rustyylex from returning two COMPLETE tokens.  */
-      pstate->prev_lexptr = pstate->lexptr;
-      return COMPLETE;
-    }
-
-  if (token != NULL)
-    return token->value;
-  if (is_gdb_var)
-    return GDBVAR;
-  return IDENT;
-}
-
-/* Lex an operator.  */
-
-int
-rust_parser::lex_operator (YYSTYPE *lvalp)
-{
-  const struct token_info *token = NULL;
-  int i;
-
-  for (i = 0; i < ARRAY_SIZE (operator_tokens); ++i)
-    {
-      if (strncmp (operator_tokens[i].name, pstate->lexptr,
-                  strlen (operator_tokens[i].name)) == 0)
-       {
-         pstate->lexptr += strlen (operator_tokens[i].name);
-         token = &operator_tokens[i];
-         break;
-       }
-    }
-
-  if (token != NULL)
-    {
-      lvalp->opcode = token->opcode;
-      return token->value;
-    }
-
-  return *pstate->lexptr++;
-}
-
-/* Lex a number.  */
-
-int
-rust_parser::lex_number (YYSTYPE *lvalp)
-{
-  regmatch_t subexps[NUM_SUBEXPRESSIONS];
-  int match;
-  int is_integer = 0;
-  int could_be_decimal = 1;
-  int implicit_i32 = 0;
-  const char *type_name = NULL;
-  struct type *type;
-  int end_index;
-  int type_index = -1;
-  int i;
-
-  match = regexec (&number_regex, pstate->lexptr, ARRAY_SIZE (subexps),
-                  subexps, 0);
-  /* Failure means the regexp is broken.  */
-  gdb_assert (match == 0);
-
-  if (subexps[INT_TEXT].rm_so != -1)
-    {
-      /* Integer part matched.  */
-      is_integer = 1;
-      end_index = subexps[INT_TEXT].rm_eo;
-      if (subexps[INT_TYPE].rm_so == -1)
-       {
-         type_name = "i32";
-         implicit_i32 = 1;
-       }
-      else
-       {
-         type_index = INT_TYPE;
-         could_be_decimal = 0;
-       }
-    }
-  else if (subexps[FLOAT_TYPE1].rm_so != -1)
-    {
-      /* Found floating point type suffix.  */
-      end_index = subexps[FLOAT_TYPE1].rm_so;
-      type_index = FLOAT_TYPE1;
-    }
-  else if (subexps[FLOAT_TYPE2].rm_so != -1)
-    {
-      /* Found floating point type suffix.  */
-      end_index = subexps[FLOAT_TYPE2].rm_so;
-      type_index = FLOAT_TYPE2;
-    }
-  else
-    {
-      /* Any other floating point match.  */
-      end_index = subexps[0].rm_eo;
-      type_name = "f64";
-    }
-
-  /* We need a special case if the final character is ".".  In this
-     case we might need to parse an integer.  For example, "23.f()" is
-     a request for a trait method call, not a syntax error involving
-     the floating point number "23.".  */
-  gdb_assert (subexps[0].rm_eo > 0);
-  if (pstate->lexptr[subexps[0].rm_eo - 1] == '.')
-    {
-      const char *next = skip_spaces (&pstate->lexptr[subexps[0].rm_eo]);
-
-      if (rust_identifier_start_p (*next) || *next == '.')
-       {
-         --subexps[0].rm_eo;
-         is_integer = 1;
-         end_index = subexps[0].rm_eo;
-         type_name = "i32";
-         could_be_decimal = 1;
-         implicit_i32 = 1;
-       }
-    }
-
-  /* Compute the type name if we haven't already.  */
-  std::string type_name_holder;
-  if (type_name == NULL)
-    {
-      gdb_assert (type_index != -1);
-      type_name_holder = std::string ((pstate->lexptr
-                                      + subexps[type_index].rm_so),
-                                     (subexps[type_index].rm_eo
-                                      - subexps[type_index].rm_so));
-      type_name = type_name_holder.c_str ();
-    }
-
-  /* Look up the type.  */
-  type = get_type (type_name);
-
-  /* Copy the text of the number and remove the "_"s.  */
-  std::string number;
-  for (i = 0; i < end_index && pstate->lexptr[i]; ++i)
-    {
-      if (pstate->lexptr[i] == '_')
-       could_be_decimal = 0;
-      else
-       number.push_back (pstate->lexptr[i]);
-    }
-
-  /* Advance past the match.  */
-  pstate->lexptr += subexps[0].rm_eo;
-
-  /* Parse the number.  */
-  if (is_integer)
-    {
-      uint64_t value;
-      int radix = 10;
-      int offset = 0;
-
-      if (number[0] == '0')
-       {
-         if (number[1] == 'x')
-           radix = 16;
-         else if (number[1] == 'o')
-           radix = 8;
-         else if (number[1] == 'b')
-           radix = 2;
-         if (radix != 10)
-           {
-             offset = 2;
-             could_be_decimal = 0;
-           }
-       }
-
-      value = strtoulst (number.c_str () + offset, NULL, radix);
-      if (implicit_i32 && value >= ((uint64_t) 1) << 31)
-       type = get_type ("i64");
-
-      lvalp->typed_val_int.val = value;
-      lvalp->typed_val_int.type = type;
-    }
-  else
-    {
-      lvalp->typed_val_float.type = type;
-      bool parsed = parse_float (number.c_str (), number.length (),
-                                lvalp->typed_val_float.type,
-                                lvalp->typed_val_float.val);
-      gdb_assert (parsed);
-    }
-
-  return is_integer ? (could_be_decimal ? DECIMAL_INTEGER : INTEGER) : FLOAT;
-}
-
-/* The lexer.  */
-
-static int
-rustyylex (YYSTYPE *lvalp, rust_parser *parser)
-{
-  struct parser_state *pstate = parser->pstate;
-
-  /* Skip all leading whitespace.  */
-  while (pstate->lexptr[0] == ' '
-        || pstate->lexptr[0] == '\t'
-        || pstate->lexptr[0] == '\r'
-        || pstate->lexptr[0] == '\n')
-    ++pstate->lexptr;
-
-  /* If we hit EOF and we're completing, then return COMPLETE -- maybe
-     we're completing an empty string at the end of a field_expr.
-     But, we don't want to return two COMPLETE tokens in a row.  */
-  if (pstate->lexptr[0] == '\0' && pstate->lexptr == pstate->prev_lexptr)
-    return 0;
-  pstate->prev_lexptr = pstate->lexptr;
-  if (pstate->lexptr[0] == '\0')
-    {
-      if (pstate->parse_completion)
-       {
-         lvalp->sval = make_stoken ("");
-         return COMPLETE;
-       }
-      return 0;
-    }
-
-  if (pstate->lexptr[0] >= '0' && pstate->lexptr[0] <= '9')
-    return parser->lex_number (lvalp);
-  else if (pstate->lexptr[0] == 'b' && pstate->lexptr[1] == '\'')
-    return parser->lex_character (lvalp);
-  else if (pstate->lexptr[0] == 'b' && pstate->lexptr[1] == '"')
-    return parser->lex_string (lvalp);
-  else if (pstate->lexptr[0] == 'b' && starts_raw_string (pstate->lexptr + 1))
-    return parser->lex_string (lvalp);
-  else if (starts_raw_string (pstate->lexptr))
-    return parser->lex_string (lvalp);
-  else if (rust_identifier_start_p (pstate->lexptr[0]))
-    return parser->lex_identifier (lvalp);
-  else if (pstate->lexptr[0] == '"')
-    return parser->lex_string (lvalp);
-  else if (pstate->lexptr[0] == '\'')
-    return parser->lex_character (lvalp);
-  else if (pstate->lexptr[0] == '}' || pstate->lexptr[0] == ']')
-    {
-      /* Falls through to lex_operator.  */
-      --parser->paren_depth;
-    }
-  else if (pstate->lexptr[0] == '(' || pstate->lexptr[0] == '{')
-    {
-      /* Falls through to lex_operator.  */
-      ++parser->paren_depth;
-    }
-  else if (pstate->lexptr[0] == ',' && pstate->comma_terminates
-          && parser->paren_depth == 0)
-    return 0;
-
-  return parser->lex_operator (lvalp);
-}
-
-/* Push back a single character to be re-lexed.  */
-
-void
-rust_parser::push_back (char c)
-{
-  /* Can't be called before any lexing.  */
-  gdb_assert (pstate->prev_lexptr != NULL);
-
-  --pstate->lexptr;
-  gdb_assert (*pstate->lexptr == c);
-}
-
-\f
-
-/* Make an arbitrary operation and fill in the fields.  */
-
-const struct rust_op *
-rust_parser::ast_operation (enum exp_opcode opcode, const struct rust_op *left,
-                           const struct rust_op *right)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = opcode;
-  result->left.op = left;
-  result->right.op = right;
-
-  return result;
-}
-
-/* Make a compound assignment operation.  */
-
-const struct rust_op *
-rust_parser::ast_compound_assignment (enum exp_opcode opcode,
-                                     const struct rust_op *left,
-                                     const struct rust_op *right)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = opcode;
-  result->compound_assignment = 1;
-  result->left.op = left;
-  result->right.op = right;
-
-  return result;
-}
-
-/* Make a typed integer literal operation.  */
-
-const struct rust_op *
-rust_parser::ast_literal (struct typed_val_int val)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = OP_LONG;
-  result->left.typed_val_int = val;
-
-  return result;
-}
-
-/* Make a typed floating point literal operation.  */
-
-const struct rust_op *
-rust_parser::ast_dliteral (struct typed_val_float val)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = OP_FLOAT;
-  result->left.typed_val_float = val;
-
-  return result;
-}
-
-/* Make a unary operation.  */
-
-const struct rust_op *
-rust_parser::ast_unary (enum exp_opcode opcode, const struct rust_op *expr)
-{
-  return ast_operation (opcode, expr, NULL);
-}
-
-/* Make a cast operation.  */
-
-const struct rust_op *
-rust_parser::ast_cast (const struct rust_op *expr, const struct rust_op *type)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = UNOP_CAST;
-  result->left.op = expr;
-  result->right.op = type;
-
-  return result;
-}
-
-/* Make a call-like operation.  This is nominally a function call, but
-   when lowering we may discover that it actually represents the
-   creation of a tuple struct.  */
-
-const struct rust_op *
-rust_parser::ast_call_ish (enum exp_opcode opcode, const struct rust_op *expr,
-                          rust_op_vector *params)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = opcode;
-  result->left.op = expr;
-  result->right.params = params;
-
-  return result;
-}
-
-/* Make a structure creation operation.  */
-
-const struct rust_op *
-rust_parser::ast_struct (const struct rust_op *name, rust_set_vector *fields)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = OP_AGGREGATE;
-  result->left.op = name;
-  result->right.field_inits = fields;
-
-  return result;
-}
-
-/* Make an identifier path.  */
-
-const struct rust_op *
-rust_parser::ast_path (struct stoken path, rust_op_vector *params)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = OP_VAR_VALUE;
-  result->left.sval = path;
-  result->right.params = params;
-
-  return result;
-}
-
-/* Make a string constant operation.  */
-
-const struct rust_op *
-rust_parser::ast_string (struct stoken str)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = OP_STRING;
-  result->left.sval = str;
-
-  return result;
-}
-
-/* Make a field expression.  */
-
-const struct rust_op *
-rust_parser::ast_structop (const struct rust_op *left, const char *name,
-                          int completing)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = STRUCTOP_STRUCT;
-  result->completing = completing;
-  result->left.op = left;
-  result->right.sval = make_stoken (name);
-
-  return result;
-}
-
-/* Make an anonymous struct operation, like 'x.0'.  */
-
-const struct rust_op *
-rust_parser::ast_structop_anonymous (const struct rust_op *left,
-                                    struct typed_val_int number)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = STRUCTOP_ANONYMOUS;
-  result->left.op = left;
-  result->right.typed_val_int = number;
-
-  return result;
-}
-
-/* Make a range operation.  */
-
-const struct rust_op *
-rust_parser::ast_range (const struct rust_op *lhs, const struct rust_op *rhs,
-                       bool inclusive)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = OP_RANGE;
-  result->inclusive = inclusive;
-  result->left.op = lhs;
-  result->right.op = rhs;
-
-  return result;
-}
-
-/* A helper function to make a type-related AST node.  */
-
-struct rust_op *
-rust_parser::ast_basic_type (enum type_code typecode)
-{
-  struct rust_op *result = OBSTACK_ZALLOC (&obstack, struct rust_op);
-
-  result->opcode = OP_TYPE;
-  result->typecode = typecode;
-  return result;
-}
-
-/* Create an AST node describing an array type.  */
-
-const struct rust_op *
-rust_parser::ast_array_type (const struct rust_op *lhs,
-                            struct typed_val_int val)
-{
-  struct rust_op *result = ast_basic_type (TYPE_CODE_ARRAY);
-
-  result->left.op = lhs;
-  result->right.typed_val_int = val;
-  return result;
-}
-
-/* Create an AST node describing a reference type.  */
-
-const struct rust_op *
-rust_parser::ast_slice_type (const struct rust_op *type)
-{
-  /* Use TYPE_CODE_COMPLEX just because it is handy.  */
-  struct rust_op *result = ast_basic_type (TYPE_CODE_COMPLEX);
-
-  result->left.op = type;
-  return result;
-}
-
-/* Create an AST node describing a reference type.  */
-
-const struct rust_op *
-rust_parser::ast_reference_type (const struct rust_op *type)
-{
-  struct rust_op *result = ast_basic_type (TYPE_CODE_REF);
-
-  result->left.op = type;
-  return result;
-}
-
-/* Create an AST node describing a pointer type.  */
-
-const struct rust_op *
-rust_parser::ast_pointer_type (const struct rust_op *type, int is_mut)
-{
-  struct rust_op *result = ast_basic_type (TYPE_CODE_PTR);
-
-  result->left.op = type;
-  /* For the time being we ignore is_mut.  */
-  return result;
-}
-
-/* Create an AST node describing a function type.  */
-
-const struct rust_op *
-rust_parser::ast_function_type (const struct rust_op *rtype,
-                               rust_op_vector *params)
-{
-  struct rust_op *result = ast_basic_type (TYPE_CODE_FUNC);
-
-  result->left.op = rtype;
-  result->right.params = params;
-  return result;
-}
-
-/* Create an AST node describing a tuple type.  */
-
-const struct rust_op *
-rust_parser::ast_tuple_type (rust_op_vector *params)
-{
-  struct rust_op *result = ast_basic_type (TYPE_CODE_STRUCT);
-
-  result->left.params = params;
-  return result;
-}
-
-/* A helper to appropriately munge NAME and BLOCK depending on the
-   presence of a leading "::".  */
-
-static void
-munge_name_and_block (const char **name, const struct block **block)
-{
-  /* If it is a global reference, skip the current block in favor of
-     the static block.  */
-  if (startswith (*name, "::"))
-    {
-      *name += 2;
-      *block = block_static_block (*block);
-    }
-}
-
-/* Like lookup_symbol, but handles Rust namespace conventions, and
-   doesn't require field_of_this_result.  */
-
-struct block_symbol
-rust_parser::lookup_symbol (const char *name, const struct block *block,
-                           const domain_enum domain)
-{
-  struct block_symbol result;
-
-  munge_name_and_block (&name, &block);
-
-  result = ::lookup_symbol (name, block, domain, NULL);
-  if (result.symbol != NULL)
-    update_innermost_block (result);
-  return result;
-}
-
-/* Look up a type, following Rust namespace conventions.  */
-
-struct type *
-rust_parser::rust_lookup_type (const char *name, const struct block *block)
-{
-  struct block_symbol result;
-  struct type *type;
-
-  munge_name_and_block (&name, &block);
-
-  result = ::lookup_symbol (name, block, STRUCT_DOMAIN, NULL);
-  if (result.symbol != NULL)
-    {
-      update_innermost_block (result);
-      return SYMBOL_TYPE (result.symbol);
-    }
-
-  type = lookup_typename (language (), name, NULL, 1);
-  if (type != NULL)
-    return type;
-
-  /* Last chance, try a built-in type.  */
-  return language_lookup_primitive_type (language (), arch (), name);
-}
-
-/* Convert a vector of rust_ops representing types to a vector of
-   types.  */
-
-std::vector<struct type *>
-rust_parser::convert_params_to_types (rust_op_vector *params)
-{
-  std::vector<struct type *> result;
-
-  if (params != nullptr)
-    {
-      for (const rust_op *op : *params)
-       result.push_back (convert_ast_to_type (op));
-    }
-
-  return result;
-}
-
-/* Convert a rust_op representing a type to a struct type *.  */
-
-struct type *
-rust_parser::convert_ast_to_type (const struct rust_op *operation)
-{
-  struct type *type, *result = NULL;
-
-  if (operation->opcode == OP_VAR_VALUE)
-    {
-      const char *varname = convert_name (operation);
-
-      result = rust_lookup_type (varname, pstate->expression_context_block);
-      if (result == NULL)
-       error (_("No typed name '%s' in current context"), varname);
-      return result;
-    }
-
-  gdb_assert (operation->opcode == OP_TYPE);
-
-  switch (operation->typecode)
-    {
-    case TYPE_CODE_ARRAY:
-      type = convert_ast_to_type (operation->left.op);
-      if (operation->right.typed_val_int.val < 0)
-       error (_("Negative array length"));
-      result = lookup_array_range_type (type, 0,
-                                       operation->right.typed_val_int.val - 1);
-      break;
-
-    case TYPE_CODE_COMPLEX:
-      {
-       struct type *usize = get_type ("usize");
-
-       type = convert_ast_to_type (operation->left.op);
-       result = rust_slice_type ("&[*gdb*]", type, usize);
-      }
-      break;
-
-    case TYPE_CODE_REF:
-    case TYPE_CODE_PTR:
-      /* For now we treat &x and *x identically.  */
-      type = convert_ast_to_type (operation->left.op);
-      result = lookup_pointer_type (type);
-      break;
-
-    case TYPE_CODE_FUNC:
-      {
-       std::vector<struct type *> args
-         (convert_params_to_types (operation->right.params));
-       struct type **argtypes = NULL;
-
-       type = convert_ast_to_type (operation->left.op);
-       if (!args.empty ())
-         argtypes = args.data ();
-
-       result
-         = lookup_function_type_with_arguments (type, args.size (),
-                                                argtypes);
-       result = lookup_pointer_type (result);
-      }
-      break;
-
-    case TYPE_CODE_STRUCT:
-      {
-       std::vector<struct type *> args
-         (convert_params_to_types (operation->left.params));
-       int i;
-       const char *name;
-
-       obstack_1grow (&obstack, '(');
-       for (i = 0; i < args.size (); ++i)
-         {
-           std::string type_name = type_to_string (args[i]);
-
-           if (i > 0)
-             obstack_1grow (&obstack, ',');
-           obstack_grow_str (&obstack, type_name.c_str ());
-         }
-
-       obstack_grow_str0 (&obstack, ")");
-       name = (const char *) obstack_finish (&obstack);
-
-       /* We don't allow creating new tuple types (yet), but we do
-          allow looking up existing tuple types.  */
-       result = rust_lookup_type (name, pstate->expression_context_block);
-       if (result == NULL)
-         error (_("could not find tuple type '%s'"), name);
-      }
-      break;
-
-    default:
-      gdb_assert_not_reached ("unhandled opcode in convert_ast_to_type");
-    }
-
-  gdb_assert (result != NULL);
-  return result;
-}
-
-/* A helper function to turn a rust_op representing a name into a full
-   name.  This applies generic arguments as needed.  The returned name
-   is allocated on the work obstack.  */
-
-const char *
-rust_parser::convert_name (const struct rust_op *operation)
-{
-  int i;
-
-  gdb_assert (operation->opcode == OP_VAR_VALUE);
-
-  if (operation->right.params == NULL)
-    return operation->left.sval.ptr;
-
-  std::vector<struct type *> types
-    (convert_params_to_types (operation->right.params));
-
-  obstack_grow_str (&obstack, operation->left.sval.ptr);
-  obstack_1grow (&obstack, '<');
-  for (i = 0; i < types.size (); ++i)
-    {
-      std::string type_name = type_to_string (types[i]);
-
-      if (i > 0)
-       obstack_1grow (&obstack, ',');
-
-      obstack_grow_str (&obstack, type_name.c_str ());
-    }
-  obstack_grow_str0 (&obstack, ">");
-
-  return (const char *) obstack_finish (&obstack);
-}
-
-/* A helper function that converts a vec of rust_ops to a gdb
-   expression.  */
-
-std::vector<expr::operation_up>
-rust_parser::convert_params_to_expression (rust_op_vector *params,
-                                          const struct rust_op *top)
-{
-  std::vector<expr::operation_up> result;
-  for (const rust_op *elem : *params)
-    result.push_back (convert_ast_to_expression (elem, top));
-  result.shrink_to_fit ();
-  return result;
-}
-
-typedef expr::operation_up binop_maker_ftype (expr::operation_up &&,
-                                             expr::operation_up &&);
-
-/* Map from an expression opcode to a function that will create an
-   instance of the appropriate operation subclass.  Only binary
-   operations are handled this way.  */
-static std::unordered_map<exp_opcode, binop_maker_ftype *,
-                         gdb::hash_enum<exp_opcode>> maker_map;
-
-/* Lower a rust_op to a gdb expression.  STATE is the parser state.
-   OPERATION is the operation to lower.  TOP is a pointer to the
-   top-most operation; it is used to handle the special case where the
-   top-most expression is an identifier and can be optionally lowered
-   to OP_TYPE.  WANT_TYPE is a flag indicating that, if the expression
-   is the name of a type, then emit an OP_TYPE for it (rather than
-   erroring).  If WANT_TYPE is set, then the similar TOP handling is
-   not done.  */
-
-expr::operation_up
-rust_parser::convert_ast_to_expression (const struct rust_op *operation,
-                                       const struct rust_op *top,
-                                       bool want_type)
-{
-  using namespace expr;
-
-  switch (operation->opcode)
-    {
-    case OP_LONG:
-      return operation_up
-       (new long_const_operation (operation->left.typed_val_int.type,
-                                  operation->left.typed_val_int.val));
-
-    case OP_FLOAT:
-      {
-       float_data data;
-       memcpy (data.data (), operation->left.typed_val_float.val,
-               sizeof (operation->left.typed_val_float.val));
-       return operation_up
-         (new float_const_operation (operation->left.typed_val_float.type,
-                                     data));
-      }
-
-    case STRUCTOP_STRUCT:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       auto result = new rust_structop (std::move (lhs),
-                                        operation->right.sval.ptr);
-       if (operation->completing)
-         pstate->mark_struct_expression (result);
-       return operation_up (result);
-      }
-
-    case STRUCTOP_ANONYMOUS:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-
-       return operation_up
-         (new rust_struct_anon (operation->right.typed_val_int.val,
-                                std::move (lhs)));
-      }
-
-    case UNOP_SIZEOF:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top,
-                                                     true);
-       return operation_up
-         (new unop_sizeof_operation (std::move (lhs)));
-      }
-
-    case UNOP_PLUS:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       return operation_up
-         (new unary_plus_operation (std::move (lhs)));
-      }
-    case UNOP_NEG:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       return operation_up
-         (new unary_neg_operation (std::move (lhs)));
-      }
-    case UNOP_COMPLEMENT:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       return operation_up
-         (new rust_unop_compl_operation (std::move (lhs)));
-      }
-    case UNOP_IND:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       return operation_up
-         (new rust_unop_ind_operation (std::move (lhs)));
-      }
-    case UNOP_ADDR:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       return operation_up
-         (new rust_unop_addr_operation (std::move (lhs)));
-      }
-
-    case BINOP_SUBSCRIPT:
-    case BINOP_MUL:
-    case BINOP_REPEAT:
-    case BINOP_DIV:
-    case BINOP_REM:
-    case BINOP_LESS:
-    case BINOP_GTR:
-    case BINOP_BITWISE_AND:
-    case BINOP_BITWISE_IOR:
-    case BINOP_BITWISE_XOR:
-    case BINOP_ADD:
-    case BINOP_SUB:
-    case BINOP_LOGICAL_OR:
-    case BINOP_LOGICAL_AND:
-    case BINOP_EQUAL:
-    case BINOP_NOTEQUAL:
-    case BINOP_LEQ:
-    case BINOP_GEQ:
-    case BINOP_LSH:
-    case BINOP_RSH:
-    case BINOP_ASSIGN:
-    case OP_RUST_ARRAY:
-      {
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       operation_up rhs = convert_ast_to_expression (operation->right.op,
-                                                     top);
-       operation_up result;
-       if (operation->compound_assignment)
-         result = (operation_up
-                   (new assign_modify_operation (operation->opcode,
-                                                 std::move (lhs),
-                                                 std::move (rhs))));
-       else
-         {
-           auto iter = maker_map.find (operation->opcode);
-           gdb_assert (iter != maker_map.end ());
-           result = iter->second (std::move (lhs), std::move (rhs));
-         }
-
-       if (operation->compound_assignment
-           || operation->opcode == BINOP_ASSIGN)
-         {
-           struct type *type
-             = language_lookup_primitive_type (pstate->language (),
-                                               pstate->gdbarch (),
-                                               "()");
-
-           operation_up nil (new long_const_operation (type, 0));
-           result.reset (new comma_operation (std::move (result),
-                                              std::move (nil)));
-         }
-
-       return result;
-      }
-
-    case UNOP_CAST:
-      {
-       struct type *type = convert_ast_to_type (operation->right.op);
-       operation_up lhs = convert_ast_to_expression (operation->left.op, top);
-       return operation_up (new unop_cast_operation (std::move (lhs), type));
-      }
-
-    case OP_FUNCALL:
-      {
-       if (operation->left.op->opcode == OP_VAR_VALUE)
-         {
-           struct type *type;
-           const char *varname = convert_name (operation->left.op);
-
-           type = rust_lookup_type (varname,
-                                    pstate->expression_context_block);
-           if (type != NULL)
-             {
-               /* This is actually a tuple struct expression, not a
-                  call expression.  */
-               rust_op_vector *params = operation->right.params;
-
-               if (type->code () != TYPE_CODE_NAMESPACE)
-                 {
-                   if (!rust_tuple_struct_type_p (type))
-                     error (_("Type %s is not a tuple struct"), varname);
-
-                   std::vector<std::pair<std::string, operation_up>> args
-                     (params->size ());
-                   for (int i = 0; i < params->size (); ++i)
-                     {
-                       char *cell = get_print_cell ();
-
-                       operation_up op
-                         = convert_ast_to_expression ((*params)[i], top);
-                       xsnprintf (cell, PRINT_CELL_SIZE, "__%d", i);
-                       args[i] = { cell, std::move (op) };
-                     }
-
-                   return make_operation<rust_aggregate_operation>
-                     (type, operation_up (), std::move (args));
-                 }
-             }
-         }
-       operation_up callee = convert_ast_to_expression (operation->left.op,
-                                                        top);
-       std::vector<operation_up> args
-         = convert_params_to_expression (operation->right.params, top);
-       return make_operation<funcall_operation> (std::move (callee),
-                                                 std::move (args));
-      }
-
-    case OP_ARRAY:
-      {
-       gdb_assert (operation->left.op == NULL);
-       std::vector<operation_up> subexps
-         = convert_params_to_expression (operation->right.params, top);
-       return make_operation<array_operation>
-         (0, operation->right.params->size () - 1, std::move (subexps));
-      }
-
-    case OP_VAR_VALUE:
-      {
-       struct block_symbol sym;
-       const char *varname;
-
-       if (operation->left.sval.ptr[0] == '$')
-         {
-           pstate->push_dollar (operation->left.sval);
-           return pstate->pop ();
-         }
-
-       varname = convert_name (operation);
-       sym = lookup_symbol (varname, pstate->expression_context_block,
-                            VAR_DOMAIN);
-       operation_up result;
-       if (sym.symbol != NULL && SYMBOL_CLASS (sym.symbol) != LOC_TYPEDEF)
-         result.reset (new var_value_operation (sym));
-       else
-         {
-           struct type *type = NULL;
-
-           if (sym.symbol != NULL)
-             {
-               gdb_assert (SYMBOL_CLASS (sym.symbol) == LOC_TYPEDEF);
-               type = SYMBOL_TYPE (sym.symbol);
-             }
-           if (type == NULL)
-             type = rust_lookup_type (varname,
-                                      pstate->expression_context_block);
-           if (type == NULL)
-             error (_("No symbol '%s' in current context"), varname);
-
-           if (!want_type
-               && type->code () == TYPE_CODE_STRUCT
-               && type->num_fields () == 0)
-             {
-               /* A unit-like struct.  */
-               result.reset (new rust_aggregate_operation (type, {}, {}));
-             }
-           else if (want_type || operation == top)
-             result.reset (new type_operation (type));
-           else
-             error (_("Found type '%s', which can't be "
-                      "evaluated in this context"),
-                    varname);
-         }
-
-       return result;
-      }
-
-    case OP_AGGREGATE:
-      {
-       rust_set_vector *fields = operation->right.field_inits;
-       struct type *type;
-       const char *name;
-
-       operation_up others;
-       std::vector<std::pair<std::string, operation_up>> field_v;
-       for (const set_field &init : *fields)
-         {
-           operation_up expr = convert_ast_to_expression (init.init, top);
-
-           if (init.name.ptr == NULL)
-             others = std::move (expr);
-           else
-             field_v.emplace_back (init.name.ptr, std::move (expr));
-         }
-
-       name = convert_name (operation->left.op);
-       type = rust_lookup_type (name, pstate->expression_context_block);
-       if (type == NULL)
-         error (_("Could not find type '%s'"), operation->left.sval.ptr);
-
-       if (type->code () != TYPE_CODE_STRUCT
-           || rust_tuple_type_p (type)
-           || rust_tuple_struct_type_p (type))
-         error (_("Struct expression applied to non-struct type"));
-
-       return operation_up
-         (new rust_aggregate_operation (type, std::move (others),
-                                        std::move (field_v)));
-      }
-
-    case OP_STRING:
-      return (operation_up
-             (new string_operation (::copy_name (operation->left.sval))));
-
-    case OP_RANGE:
-      {
-       enum range_flag kind = (RANGE_HIGH_BOUND_DEFAULT
-                               | RANGE_LOW_BOUND_DEFAULT);
-       operation_up lhs, rhs;
-
-       if (operation->left.op != NULL)
-         {
-           lhs = convert_ast_to_expression (operation->left.op, top);
-           kind &= ~RANGE_LOW_BOUND_DEFAULT;
-         }
-       if (operation->right.op != NULL)
-         {
-           rhs = convert_ast_to_expression (operation->right.op, top);
-           if (kind == (RANGE_HIGH_BOUND_DEFAULT | RANGE_LOW_BOUND_DEFAULT))
-             {
-               kind = RANGE_LOW_BOUND_DEFAULT;
-               if (!operation->inclusive)
-                 kind |= RANGE_HIGH_BOUND_EXCLUSIVE;
-             }
-           else
-             {
-               gdb_assert (kind == RANGE_HIGH_BOUND_DEFAULT);
-               kind = RANGE_STANDARD;
-               if (!operation->inclusive)
-                 kind |= RANGE_HIGH_BOUND_EXCLUSIVE;
-             }
-         }
-       else
-         {
-           /* Nothing should make an inclusive range without an upper
-              bound.  */
-           gdb_assert (!operation->inclusive);
-         }
-
-       return operation_up (new rust_range_operation (kind,
-                                                      std::move (lhs),
-                                                      std::move (rhs)));
-      }
-
-    default:
-      gdb_assert_not_reached ("unhandled opcode in convert_ast_to_expression");
-    }
-}
-
-\f
-
-/* The parser as exposed to gdb.  */
-
-int
-rust_language::parser (struct parser_state *state) const
-{
-  int result;
-
-  /* This sets various globals and also clears them on
-     destruction.  */
-  rust_parser parser (state);
-
-  result = rustyyparse (&parser);
-
-  if (!result || (state->parse_completion && parser.rust_ast != NULL))
-    {
-      expr::operation_up op
-       = parser.convert_ast_to_expression (parser.rust_ast, parser.rust_ast);
-      state->set_operation (std::move (op));
-    }
-
-  return result;
-}
-
-/* The parser error handler.  */
-
-static void
-rustyyerror (rust_parser *parser, const char *msg)
-{
-  const char *where = (parser->pstate->prev_lexptr
-                      ? parser->pstate->prev_lexptr
-                      : parser->pstate->lexptr);
-  error (_("%s in expression, near `%s'."), msg, where);
-}
-
-\f
-
-#if GDB_SELF_TEST
-
-/* Initialize the lexer for testing.  */
-
-static void
-rust_lex_test_init (rust_parser *parser, const char *input)
-{
-  parser->pstate->prev_lexptr = NULL;
-  parser->pstate->lexptr = input;
-  parser->paren_depth = 0;
-}
-
-/* A test helper that lexes a string, expecting a single token.  It
-   returns the lexer data for this token.  */
-
-static RUSTSTYPE
-rust_lex_test_one (rust_parser *parser, const char *input, int expected)
-{
-  int token;
-  RUSTSTYPE result;
-
-  rust_lex_test_init (parser, input);
-
-  token = rustyylex (&result, parser);
-  SELF_CHECK (token == expected);
-
-  if (token)
-    {
-      RUSTSTYPE ignore;
-      token = rustyylex (&ignore, parser);
-      SELF_CHECK (token == 0);
-    }
-
-  return result;
-}
-
-/* Test that INPUT lexes as the integer VALUE.  */
-
-static void
-rust_lex_int_test (rust_parser *parser, const char *input,
-                  LONGEST value, int kind)
-{
-  RUSTSTYPE result = rust_lex_test_one (parser, input, kind);
-  SELF_CHECK (result.typed_val_int.val == value);
-}
-
-/* Test that INPUT throws an exception with text ERR.  */
-
-static void
-rust_lex_exception_test (rust_parser *parser, const char *input,
-                        const char *err)
-{
-  try
-    {
-      /* The "kind" doesn't matter.  */
-      rust_lex_test_one (parser, input, DECIMAL_INTEGER);
-      SELF_CHECK (0);
-    }
-  catch (const gdb_exception_error &except)
-    {
-      SELF_CHECK (strcmp (except.what (), err) == 0);
-    }
-}
-
-/* Test that INPUT lexes as the identifier, string, or byte-string
-   VALUE.  KIND holds the expected token kind.  */
-
-static void
-rust_lex_stringish_test (rust_parser *parser, const char *input,
-                        const char *value, int kind)
-{
-  RUSTSTYPE result = rust_lex_test_one (parser, input, kind);
-  SELF_CHECK (result.sval.length == strlen (value));
-  SELF_CHECK (strncmp (result.sval.ptr, value, result.sval.length) == 0);
-}
-
-/* Helper to test that a string parses as a given token sequence.  */
-
-static void
-rust_lex_test_sequence (rust_parser *parser, const char *input, int len,
-                       const int expected[])
-{
-  int i;
-
-  parser->pstate->lexptr = input;
-  parser->paren_depth = 0;
-
-  for (i = 0; i < len; ++i)
-    {
-      RUSTSTYPE ignore;
-      int token = rustyylex (&ignore, parser);
-
-      SELF_CHECK (token == expected[i]);
-    }
-}
-
-/* Tests for an integer-parsing corner case.  */
-
-static void
-rust_lex_test_trailing_dot (rust_parser *parser)
-{
-  const int expected1[] = { DECIMAL_INTEGER, '.', IDENT, '(', ')', 0 };
-  const int expected2[] = { INTEGER, '.', IDENT, '(', ')', 0 };
-  const int expected3[] = { FLOAT, EQEQ, '(', ')', 0 };
-  const int expected4[] = { DECIMAL_INTEGER, DOTDOT, DECIMAL_INTEGER, 0 };
-
-  rust_lex_test_sequence (parser, "23.g()", ARRAY_SIZE (expected1), expected1);
-  rust_lex_test_sequence (parser, "23_0.g()", ARRAY_SIZE (expected2),
-                         expected2);
-  rust_lex_test_sequence (parser, "23.==()", ARRAY_SIZE (expected3),
-                         expected3);
-  rust_lex_test_sequence (parser, "23..25", ARRAY_SIZE (expected4), expected4);
-}
-
-/* Tests of completion.  */
-
-static void
-rust_lex_test_completion (rust_parser *parser)
-{
-  const int expected[] = { IDENT, '.', COMPLETE, 0 };
-
-  parser->pstate->parse_completion = 1;
-
-  rust_lex_test_sequence (parser, "something.wha", ARRAY_SIZE (expected),
-                         expected);
-  rust_lex_test_sequence (parser, "something.", ARRAY_SIZE (expected),
-                         expected);
-
-  parser->pstate->parse_completion = 0;
-}
-
-/* Test pushback.  */
-
-static void
-rust_lex_test_push_back (rust_parser *parser)
-{
-  int token;
-  RUSTSTYPE lval;
-
-  rust_lex_test_init (parser, ">>=");
-
-  token = rustyylex (&lval, parser);
-  SELF_CHECK (token == COMPOUND_ASSIGN);
-  SELF_CHECK (lval.opcode == BINOP_RSH);
-
-  parser->push_back ('=');
-
-  token = rustyylex (&lval, parser);
-  SELF_CHECK (token == '=');
-
-  token = rustyylex (&lval, parser);
-  SELF_CHECK (token == 0);
-}
-
-/* Unit test the lexer.  */
-
-static void
-rust_lex_tests (void)
-{
-  int i;
-
-  /* Set up dummy "parser", so that rust_type works.  */
-  struct parser_state ps (language_def (language_rust), target_gdbarch (),
-                         nullptr, 0, 0, nullptr, 0, nullptr, false);
-  rust_parser parser (&ps);
-
-  rust_lex_test_one (&parser, "", 0);
-  rust_lex_test_one (&parser, "    \t  \n \r  ", 0);
-  rust_lex_test_one (&parser, "thread 23", 0);
-  rust_lex_test_one (&parser, "task 23", 0);
-  rust_lex_test_one (&parser, "th 104", 0);
-  rust_lex_test_one (&parser, "ta 97", 0);
-
-  rust_lex_int_test (&parser, "'z'", 'z', INTEGER);
-  rust_lex_int_test (&parser, "'\\xff'", 0xff, INTEGER);
-  rust_lex_int_test (&parser, "'\\u{1016f}'", 0x1016f, INTEGER);
-  rust_lex_int_test (&parser, "b'z'", 'z', INTEGER);
-  rust_lex_int_test (&parser, "b'\\xfe'", 0xfe, INTEGER);
-  rust_lex_int_test (&parser, "b'\\xFE'", 0xfe, INTEGER);
-  rust_lex_int_test (&parser, "b'\\xfE'", 0xfe, INTEGER);
-
-  /* Test all escapes in both modes.  */
-  rust_lex_int_test (&parser, "'\\n'", '\n', INTEGER);
-  rust_lex_int_test (&parser, "'\\r'", '\r', INTEGER);
-  rust_lex_int_test (&parser, "'\\t'", '\t', INTEGER);
-  rust_lex_int_test (&parser, "'\\\\'", '\\', INTEGER);
-  rust_lex_int_test (&parser, "'\\0'", '\0', INTEGER);
-  rust_lex_int_test (&parser, "'\\''", '\'', INTEGER);
-  rust_lex_int_test (&parser, "'\\\"'", '"', INTEGER);
-
-  rust_lex_int_test (&parser, "b'\\n'", '\n', INTEGER);
-  rust_lex_int_test (&parser, "b'\\r'", '\r', INTEGER);
-  rust_lex_int_test (&parser, "b'\\t'", '\t', INTEGER);
-  rust_lex_int_test (&parser, "b'\\\\'", '\\', INTEGER);
-  rust_lex_int_test (&parser, "b'\\0'", '\0', INTEGER);
-  rust_lex_int_test (&parser, "b'\\''", '\'', INTEGER);
-  rust_lex_int_test (&parser, "b'\\\"'", '"', INTEGER);
-
-  rust_lex_exception_test (&parser, "'z", "Unterminated character literal");
-  rust_lex_exception_test (&parser, "b'\\x0'", "Not enough hex digits seen");
-  rust_lex_exception_test (&parser, "b'\\u{0}'",
-                          "Unicode escape in byte literal");
-  rust_lex_exception_test (&parser, "'\\x0'", "Not enough hex digits seen");
-  rust_lex_exception_test (&parser, "'\\u0'", "Missing '{' in Unicode escape");
-  rust_lex_exception_test (&parser, "'\\u{0", "Missing '}' in Unicode escape");
-  rust_lex_exception_test (&parser, "'\\u{0000007}", "Overlong hex escape");
-  rust_lex_exception_test (&parser, "'\\u{}", "Not enough hex digits seen");
-  rust_lex_exception_test (&parser, "'\\Q'", "Invalid escape \\Q in literal");
-  rust_lex_exception_test (&parser, "b'\\Q'", "Invalid escape \\Q in literal");
-
-  rust_lex_int_test (&parser, "23", 23, DECIMAL_INTEGER);
-  rust_lex_int_test (&parser, "2_344__29", 234429, INTEGER);
-  rust_lex_int_test (&parser, "0x1f", 0x1f, INTEGER);
-  rust_lex_int_test (&parser, "23usize", 23, INTEGER);
-  rust_lex_int_test (&parser, "23i32", 23, INTEGER);
-  rust_lex_int_test (&parser, "0x1_f", 0x1f, INTEGER);
-  rust_lex_int_test (&parser, "0b1_101011__", 0x6b, INTEGER);
-  rust_lex_int_test (&parser, "0o001177i64", 639, INTEGER);
-  rust_lex_int_test (&parser, "0x123456789u64", 0x123456789ull, INTEGER);
-
-  rust_lex_test_trailing_dot (&parser);
-
-  rust_lex_test_one (&parser, "23.", FLOAT);
-  rust_lex_test_one (&parser, "23.99f32", FLOAT);
-  rust_lex_test_one (&parser, "23e7", FLOAT);
-  rust_lex_test_one (&parser, "23E-7", FLOAT);
-  rust_lex_test_one (&parser, "23e+7", FLOAT);
-  rust_lex_test_one (&parser, "23.99e+7f64", FLOAT);
-  rust_lex_test_one (&parser, "23.82f32", FLOAT);
-
-  rust_lex_stringish_test (&parser, "hibob", "hibob", IDENT);
-  rust_lex_stringish_test (&parser, "hibob__93", "hibob__93", IDENT);
-  rust_lex_stringish_test (&parser, "thread", "thread", IDENT);
-
-  rust_lex_stringish_test (&parser, "\"string\"", "string", STRING);
-  rust_lex_stringish_test (&parser, "\"str\\ting\"", "str\ting", STRING);
-  rust_lex_stringish_test (&parser, "\"str\\\"ing\"", "str\"ing", STRING);
-  rust_lex_stringish_test (&parser, "r\"str\\ing\"", "str\\ing", STRING);
-  rust_lex_stringish_test (&parser, "r#\"str\\ting\"#", "str\\ting", STRING);
-  rust_lex_stringish_test (&parser, "r###\"str\\\"ing\"###", "str\\\"ing",
-                          STRING);
-
-  rust_lex_stringish_test (&parser, "b\"string\"", "string", BYTESTRING);
-  rust_lex_stringish_test (&parser, "b\"\x73tring\"", "string", BYTESTRING);
-  rust_lex_stringish_test (&parser, "b\"str\\\"ing\"", "str\"ing", BYTESTRING);
-  rust_lex_stringish_test (&parser, "br####\"\\x73tring\"####", "\\x73tring",
-                          BYTESTRING);
-
-  for (i = 0; i < ARRAY_SIZE (identifier_tokens); ++i)
-    rust_lex_test_one (&parser, identifier_tokens[i].name,
-                      identifier_tokens[i].value);
-
-  for (i = 0; i < ARRAY_SIZE (operator_tokens); ++i)
-    rust_lex_test_one (&parser, operator_tokens[i].name,
-                      operator_tokens[i].value);
-
-  rust_lex_test_completion (&parser);
-  rust_lex_test_push_back (&parser);
-}
-
-#endif /* GDB_SELF_TEST */
-
-void _initialize_rust_exp ();
-void
-_initialize_rust_exp ()
-{
-  int code = regcomp (&number_regex, number_regex_text, REG_EXTENDED);
-  /* If the regular expression was incorrect, it was a programming
-     error.  */
-  gdb_assert (code == 0);
-
-  using namespace expr;
-  maker_map[BINOP_SUBSCRIPT] = make_operation<rust_subscript_operation>;
-  maker_map[BINOP_MUL] = make_operation<mul_operation>;
-  maker_map[BINOP_REPEAT] = make_operation<repeat_operation>;
-  maker_map[BINOP_DIV] = make_operation<div_operation>;
-  maker_map[BINOP_REM] = make_operation<rem_operation>;
-  maker_map[BINOP_LESS] = make_operation<less_operation>;
-  maker_map[BINOP_GTR] = make_operation<gtr_operation>;
-  maker_map[BINOP_BITWISE_AND] = make_operation<bitwise_and_operation>;
-  maker_map[BINOP_BITWISE_IOR] = make_operation<bitwise_ior_operation>;
-  maker_map[BINOP_BITWISE_XOR] = make_operation<bitwise_xor_operation>;
-  maker_map[BINOP_ADD] = make_operation<add_operation>;
-  maker_map[BINOP_SUB] = make_operation<sub_operation>;
-  maker_map[BINOP_LOGICAL_OR] = make_operation<logical_or_operation>;
-  maker_map[BINOP_LOGICAL_AND] = make_operation<logical_and_operation>;
-  maker_map[BINOP_EQUAL] = make_operation<equal_operation>;
-  maker_map[BINOP_NOTEQUAL] = make_operation<notequal_operation>;
-  maker_map[BINOP_LEQ] = make_operation<leq_operation>;
-  maker_map[BINOP_GEQ] = make_operation<geq_operation>;
-  maker_map[BINOP_LSH] = make_operation<lsh_operation>;
-  maker_map[BINOP_RSH] = make_operation<rsh_operation>;
-  maker_map[BINOP_ASSIGN] = make_operation<assign_operation>;
-  maker_map[OP_RUST_ARRAY] = make_operation<rust_array_operation>;
-
-#if GDB_SELF_TEST
-  selftests::register_test ("rust-lex", rust_lex_tests);
-#endif
-}
diff --git a/gdb/rust-parse.c b/gdb/rust-parse.c
new file mode 100644 (file)
index 0000000..bb31782
--- /dev/null
@@ -0,0 +1,2351 @@
+/* Rust expression parsing for GDB, the GNU debugger.
+
+   Copyright (C) 2016-2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "block.h"
+#include "charset.h"
+#include "cp-support.h"
+#include "gdb_obstack.h"
+#include "gdb_regex.h"
+#include "rust-lang.h"
+#include "parser-defs.h"
+#include "gdbsupport/selftest.h"
+#include "value.h"
+#include "gdbarch.h"
+#include "rust-exp.h"
+
+using namespace expr;
+
+/* A regular expression for matching Rust numbers.  This is split up
+   since it is very long and this gives us a way to comment the
+   sections.  */
+
+static const char number_regex_text[] =
+  /* subexpression 1: allows use of alternation, otherwise uninteresting */
+  "^("
+  /* First comes floating point.  */
+  /* Recognize number after the decimal point, with optional
+     exponent and optional type suffix.
+     subexpression 2: allows "?", otherwise uninteresting
+     subexpression 3: if present, type suffix
+  */
+  "[0-9][0-9_]*\\.[0-9][0-9_]*([eE][-+]?[0-9][0-9_]*)?(f32|f64)?"
+#define FLOAT_TYPE1 3
+  "|"
+  /* Recognize exponent without decimal point, with optional type
+     suffix.
+     subexpression 4: if present, type suffix
+  */
+#define FLOAT_TYPE2 4
+  "[0-9][0-9_]*[eE][-+]?[0-9][0-9_]*(f32|f64)?"
+  "|"
+  /* "23." is a valid floating point number, but "23.e5" and
+     "23.f32" are not.  So, handle the trailing-. case
+     separately.  */
+  "[0-9][0-9_]*\\."
+  "|"
+  /* Finally come integers.
+     subexpression 5: text of integer
+     subexpression 6: if present, type suffix
+     subexpression 7: allows use of alternation, otherwise uninteresting
+  */
+#define INT_TEXT 5
+#define INT_TYPE 6
+  "(0x[a-fA-F0-9_]+|0o[0-7_]+|0b[01_]+|[0-9][0-9_]*)"
+  "([iu](size|8|16|32|64))?"
+  ")";
+/* The number of subexpressions to allocate space for, including the
+   "0th" whole match subexpression.  */
+#define NUM_SUBEXPRESSIONS 8
+
+/* The compiled number-matching regex.  */
+
+static regex_t number_regex;
+
+/* The kinds of tokens.  Note that single-character tokens are
+   represented by themselves, so for instance '[' is a token.  */
+enum token_type : int
+{
+  /* Make sure to start after any ASCII character.  */
+  GDBVAR = 256,
+  IDENT,
+  COMPLETE,
+  INTEGER,
+  DECIMAL_INTEGER,
+  STRING,
+  BYTESTRING,
+  FLOAT,
+  COMPOUND_ASSIGN,
+
+  /* Keyword tokens.  */
+  KW_AS,
+  KW_IF,
+  KW_TRUE,
+  KW_FALSE,
+  KW_SUPER,
+  KW_SELF,
+  KW_MUT,
+  KW_EXTERN,
+  KW_CONST,
+  KW_FN,
+  KW_SIZEOF,
+
+  /* Operator tokens.  */
+  DOTDOT,
+  DOTDOTEQ,
+  OROR,
+  ANDAND,
+  EQEQ,
+  NOTEQ,
+  LTEQ,
+  GTEQ,
+  LSH,
+  RSH,
+  COLONCOLON,
+  ARROW,
+};
+
+/* A typed integer constant.  */
+
+struct typed_val_int
+{
+  LONGEST val;
+  struct type *type;
+};
+
+/* A typed floating point constant.  */
+
+struct typed_val_float
+{
+  float_data val;
+  struct type *type;
+};
+
+/* A struct of this type is used to describe a token.  */
+
+struct token_info
+{
+  const char *name;
+  int value;
+  enum exp_opcode opcode;
+};
+
+/* Identifier tokens.  */
+
+static const struct token_info identifier_tokens[] =
+{
+  { "as", KW_AS, OP_NULL },
+  { "false", KW_FALSE, OP_NULL },
+  { "if", 0, OP_NULL },
+  { "mut", KW_MUT, OP_NULL },
+  { "const", KW_CONST, OP_NULL },
+  { "self", KW_SELF, OP_NULL },
+  { "super", KW_SUPER, OP_NULL },
+  { "true", KW_TRUE, OP_NULL },
+  { "extern", KW_EXTERN, OP_NULL },
+  { "fn", KW_FN, OP_NULL },
+  { "sizeof", KW_SIZEOF, OP_NULL },
+};
+
+/* Operator tokens, sorted longest first.  */
+
+static const struct token_info operator_tokens[] =
+{
+  { ">>=", COMPOUND_ASSIGN, BINOP_RSH },
+  { "<<=", COMPOUND_ASSIGN, BINOP_LSH },
+
+  { "<<", LSH, OP_NULL },
+  { ">>", RSH, OP_NULL },
+  { "&&", ANDAND, OP_NULL },
+  { "||", OROR, OP_NULL },
+  { "==", EQEQ, OP_NULL },
+  { "!=", NOTEQ, OP_NULL },
+  { "<=", LTEQ, OP_NULL },
+  { ">=", GTEQ, OP_NULL },
+  { "+=", COMPOUND_ASSIGN, BINOP_ADD },
+  { "-=", COMPOUND_ASSIGN, BINOP_SUB },
+  { "*=", COMPOUND_ASSIGN, BINOP_MUL },
+  { "/=", COMPOUND_ASSIGN, BINOP_DIV },
+  { "%=", COMPOUND_ASSIGN, BINOP_REM },
+  { "&=", COMPOUND_ASSIGN, BINOP_BITWISE_AND },
+  { "|=", COMPOUND_ASSIGN, BINOP_BITWISE_IOR },
+  { "^=", COMPOUND_ASSIGN, BINOP_BITWISE_XOR },
+  { "..=", DOTDOTEQ, OP_NULL },
+
+  { "::", COLONCOLON, OP_NULL },
+  { "..", DOTDOT, OP_NULL },
+  { "->", ARROW, OP_NULL }
+};
+
+/* An instance of this is created before parsing, and destroyed when
+   parsing is finished.  */
+
+struct rust_parser
+{
+  explicit rust_parser (struct parser_state *state)
+    : pstate (state)
+  {
+  }
+
+  DISABLE_COPY_AND_ASSIGN (rust_parser);
+
+  /* Return the parser's language.  */
+  const struct language_defn *language () const
+  {
+    return pstate->language ();
+  }
+
+  /* Return the parser's gdbarch.  */
+  struct gdbarch *arch () const
+  {
+    return pstate->gdbarch ();
+  }
+
+  /* A helper to look up a Rust type, or fail.  This only works for
+     types defined by rust_language_arch_info.  */
+
+  struct type *get_type (const char *name)
+  {
+    struct type *type;
+
+    type = language_lookup_primitive_type (language (), arch (), name);
+    if (type == NULL)
+      error (_("Could not find Rust type %s"), name);
+    return type;
+  }
+
+  std::string crate_name (const std::string &name);
+  std::string super_name (const std::string &ident, unsigned int n_supers);
+
+  int lex_character ();
+  int lex_number ();
+  int lex_string ();
+  int lex_identifier ();
+  uint32_t lex_hex (int min, int max);
+  uint32_t lex_escape (int is_byte);
+  int lex_operator ();
+  int lex_one_token ();
+  void push_back (char c);
+
+  /* The main interface to lexing.  Lexes one token and updates the
+     internal state.  */
+  void lex ()
+  {
+    current_token = lex_one_token ();
+  }
+
+  /* Assuming the current token is TYPE, lex the next token.  */
+  void assume (int type)
+  {
+    gdb_assert (current_token == type);
+    lex ();
+  }
+
+  /* Require the single-character token C, and lex the next token; or
+     throw an exception.  */
+  void require (char type)
+  {
+    if (current_token != type)
+      error (_("'%c' expected"), type);
+    lex ();
+  }
+
+  /* Entry point for all parsing.  */
+  operation_up parse_entry_point ()
+  {
+    lex ();
+    return parse_expr ();
+  }
+
+  operation_up parse_tuple ();
+  operation_up parse_array ();
+  operation_up name_to_operation (const std::string &name);
+  operation_up parse_struct_expr (struct type *type);
+  operation_up parse_binop (bool required);
+  operation_up parse_range ();
+  operation_up parse_expr ();
+  operation_up parse_sizeof ();
+  operation_up parse_addr ();
+  operation_up parse_field (operation_up &&);
+  operation_up parse_index (operation_up &&);
+  std::vector<operation_up> parse_paren_args ();
+  operation_up parse_call (operation_up &&);
+  std::vector<struct type *> parse_type_list ();
+  std::vector<struct type *> parse_maybe_type_list ();
+  struct type *parse_array_type ();
+  struct type *parse_slice_type ();
+  struct type *parse_pointer_type ();
+  struct type *parse_function_type ();
+  struct type *parse_tuple_type ();
+  struct type *parse_type ();
+  std::string parse_path (bool for_expr);
+  operation_up parse_string ();
+  operation_up parse_tuple_struct (struct type *type);
+  operation_up parse_path_expr ();
+  operation_up parse_atom (bool required);
+
+  void update_innermost_block (struct block_symbol sym);
+  struct block_symbol lookup_symbol (const char *name,
+                                    const struct block *block,
+                                    const domain_enum domain);
+  struct type *rust_lookup_type (const char *name);
+
+  /* Clear some state.  This is only used for testing.  */
+#if GDB_SELF_TEST
+  void reset (const char *input)
+  {
+    pstate->prev_lexptr = nullptr;
+    pstate->lexptr = input;
+    paren_depth = 0;
+    current_token = 0;
+    current_int_val = {};
+    current_float_val = {};
+    current_string_val = {};
+    current_opcode = OP_NULL;
+  }
+#endif /* GDB_SELF_TEST */
+
+  /* Return the token's string value as a string.  */
+  std::string get_string () const
+  {
+    return std::string (current_string_val.ptr, current_string_val.length);
+  }
+
+  /* A pointer to this is installed globally.  */
+  auto_obstack obstack;
+
+  /* The parser state gdb gave us.  */
+  struct parser_state *pstate;
+
+  /* Depth of parentheses.  */
+  int paren_depth = 0;
+
+  /* The current token's type.  */
+  int current_token = 0;
+  /* The current token's payload, if any.  */
+  typed_val_int current_int_val {};
+  typed_val_float current_float_val {};
+  struct stoken current_string_val {};
+  enum exp_opcode current_opcode = OP_NULL;
+
+  /* When completing, this may be set to the field operation to
+     complete.  */
+  operation_up completion_op;
+};
+
+/* Return an string referring to NAME, but relative to the crate's
+   name.  */
+
+std::string
+rust_parser::crate_name (const std::string &name)
+{
+  std::string crate = rust_crate_for_block (pstate->expression_context_block);
+
+  if (crate.empty ())
+    error (_("Could not find crate for current location"));
+  return "::" + crate + "::" + name;
+}
+
+/* Return a string referring to a "super::" qualified name.  IDENT is
+   the base name and N_SUPERS is how many "super::"s were provided.
+   N_SUPERS can be zero.  */
+
+std::string
+rust_parser::super_name (const std::string &ident, unsigned int n_supers)
+{
+  const char *scope = block_scope (pstate->expression_context_block);
+  int offset;
+
+  if (scope[0] == '\0')
+    error (_("Couldn't find namespace scope for self::"));
+
+  if (n_supers > 0)
+    {
+      int len;
+      std::vector<int> offsets;
+      unsigned int current_len;
+
+      current_len = cp_find_first_component (scope);
+      while (scope[current_len] != '\0')
+       {
+         offsets.push_back (current_len);
+         gdb_assert (scope[current_len] == ':');
+         /* The "::".  */
+         current_len += 2;
+         current_len += cp_find_first_component (scope
+                                                 + current_len);
+       }
+
+      len = offsets.size ();
+      if (n_supers >= len)
+       error (_("Too many super:: uses from '%s'"), scope);
+
+      offset = offsets[len - n_supers];
+    }
+  else
+    offset = strlen (scope);
+
+  return "::" + std::string (scope, offset) + "::" + ident;
+}
+
+/* A helper to appropriately munge NAME and BLOCK depending on the
+   presence of a leading "::".  */
+
+static void
+munge_name_and_block (const char **name, const struct block **block)
+{
+  /* If it is a global reference, skip the current block in favor of
+     the static block.  */
+  if (startswith (*name, "::"))
+    {
+      *name += 2;
+      *block = block_static_block (*block);
+    }
+}
+
+/* Like lookup_symbol, but handles Rust namespace conventions, and
+   doesn't require field_of_this_result.  */
+
+struct block_symbol
+rust_parser::lookup_symbol (const char *name, const struct block *block,
+                           const domain_enum domain)
+{
+  struct block_symbol result;
+
+  munge_name_and_block (&name, &block);
+
+  result = ::lookup_symbol (name, block, domain, NULL);
+  if (result.symbol != NULL)
+    update_innermost_block (result);
+  return result;
+}
+
+/* Look up a type, following Rust namespace conventions.  */
+
+struct type *
+rust_parser::rust_lookup_type (const char *name)
+{
+  struct block_symbol result;
+  struct type *type;
+
+  const struct block *block = pstate->expression_context_block;
+  munge_name_and_block (&name, &block);
+
+  result = ::lookup_symbol (name, block, STRUCT_DOMAIN, NULL);
+  if (result.symbol != NULL)
+    {
+      update_innermost_block (result);
+      return SYMBOL_TYPE (result.symbol);
+    }
+
+  type = lookup_typename (language (), name, NULL, 1);
+  if (type != NULL)
+    return type;
+
+  /* Last chance, try a built-in type.  */
+  return language_lookup_primitive_type (language (), arch (), name);
+}
+
+/* A helper that updates the innermost block as appropriate.  */
+
+void
+rust_parser::update_innermost_block (struct block_symbol sym)
+{
+  if (symbol_read_needs_frame (sym.symbol))
+    pstate->block_tracker->update (sym);
+}
+
+/* Lex a hex number with at least MIN digits and at most MAX
+   digits.  */
+
+uint32_t
+rust_parser::lex_hex (int min, int max)
+{
+  uint32_t result = 0;
+  int len = 0;
+  /* We only want to stop at MAX if we're lexing a byte escape.  */
+  int check_max = min == max;
+
+  while ((check_max ? len <= max : 1)
+        && ((pstate->lexptr[0] >= 'a' && pstate->lexptr[0] <= 'f')
+            || (pstate->lexptr[0] >= 'A' && pstate->lexptr[0] <= 'F')
+            || (pstate->lexptr[0] >= '0' && pstate->lexptr[0] <= '9')))
+    {
+      result *= 16;
+      if (pstate->lexptr[0] >= 'a' && pstate->lexptr[0] <= 'f')
+       result = result + 10 + pstate->lexptr[0] - 'a';
+      else if (pstate->lexptr[0] >= 'A' && pstate->lexptr[0] <= 'F')
+       result = result + 10 + pstate->lexptr[0] - 'A';
+      else
+       result = result + pstate->lexptr[0] - '0';
+      ++pstate->lexptr;
+      ++len;
+    }
+
+  if (len < min)
+    error (_("Not enough hex digits seen"));
+  if (len > max)
+    {
+      gdb_assert (min != max);
+      error (_("Overlong hex escape"));
+    }
+
+  return result;
+}
+
+/* Lex an escape.  IS_BYTE is true if we're lexing a byte escape;
+   otherwise we're lexing a character escape.  */
+
+uint32_t
+rust_parser::lex_escape (int is_byte)
+{
+  uint32_t result;
+
+  gdb_assert (pstate->lexptr[0] == '\\');
+  ++pstate->lexptr;
+  switch (pstate->lexptr[0])
+    {
+    case 'x':
+      ++pstate->lexptr;
+      result = lex_hex (2, 2);
+      break;
+
+    case 'u':
+      if (is_byte)
+       error (_("Unicode escape in byte literal"));
+      ++pstate->lexptr;
+      if (pstate->lexptr[0] != '{')
+       error (_("Missing '{' in Unicode escape"));
+      ++pstate->lexptr;
+      result = lex_hex (1, 6);
+      /* Could do range checks here.  */
+      if (pstate->lexptr[0] != '}')
+       error (_("Missing '}' in Unicode escape"));
+      ++pstate->lexptr;
+      break;
+
+    case 'n':
+      result = '\n';
+      ++pstate->lexptr;
+      break;
+    case 'r':
+      result = '\r';
+      ++pstate->lexptr;
+      break;
+    case 't':
+      result = '\t';
+      ++pstate->lexptr;
+      break;
+    case '\\':
+      result = '\\';
+      ++pstate->lexptr;
+      break;
+    case '0':
+      result = '\0';
+      ++pstate->lexptr;
+      break;
+    case '\'':
+      result = '\'';
+      ++pstate->lexptr;
+      break;
+    case '"':
+      result = '"';
+      ++pstate->lexptr;
+      break;
+
+    default:
+      error (_("Invalid escape \\%c in literal"), pstate->lexptr[0]);
+    }
+
+  return result;
+}
+
+/* Lex a character constant.  */
+
+int
+rust_parser::lex_character ()
+{
+  int is_byte = 0;
+  uint32_t value;
+
+  if (pstate->lexptr[0] == 'b')
+    {
+      is_byte = 1;
+      ++pstate->lexptr;
+    }
+  gdb_assert (pstate->lexptr[0] == '\'');
+  ++pstate->lexptr;
+  /* This should handle UTF-8 here.  */
+  if (pstate->lexptr[0] == '\\')
+    value = lex_escape (is_byte);
+  else
+    {
+      value = pstate->lexptr[0] & 0xff;
+      ++pstate->lexptr;
+    }
+
+  if (pstate->lexptr[0] != '\'')
+    error (_("Unterminated character literal"));
+  ++pstate->lexptr;
+
+  current_int_val.val = value;
+  current_int_val.type = get_type (is_byte ? "u8" : "char");
+
+  return INTEGER;
+}
+
+/* Return the offset of the double quote if STR looks like the start
+   of a raw string, or 0 if STR does not start a raw string.  */
+
+static int
+starts_raw_string (const char *str)
+{
+  const char *save = str;
+
+  if (str[0] != 'r')
+    return 0;
+  ++str;
+  while (str[0] == '#')
+    ++str;
+  if (str[0] == '"')
+    return str - save;
+  return 0;
+}
+
+/* Return true if STR looks like the end of a raw string that had N
+   hashes at the start.  */
+
+static bool
+ends_raw_string (const char *str, int n)
+{
+  int i;
+
+  gdb_assert (str[0] == '"');
+  for (i = 0; i < n; ++i)
+    if (str[i + 1] != '#')
+      return false;
+  return true;
+}
+
+/* Lex a string constant.  */
+
+int
+rust_parser::lex_string ()
+{
+  int is_byte = pstate->lexptr[0] == 'b';
+  int raw_length;
+
+  if (is_byte)
+    ++pstate->lexptr;
+  raw_length = starts_raw_string (pstate->lexptr);
+  pstate->lexptr += raw_length;
+  gdb_assert (pstate->lexptr[0] == '"');
+  ++pstate->lexptr;
+
+  while (1)
+    {
+      uint32_t value;
+
+      if (raw_length > 0)
+       {
+         if (pstate->lexptr[0] == '"' && ends_raw_string (pstate->lexptr,
+                                                          raw_length - 1))
+           {
+             /* Exit with lexptr pointing after the final "#".  */
+             pstate->lexptr += raw_length;
+             break;
+           }
+         else if (pstate->lexptr[0] == '\0')
+           error (_("Unexpected EOF in string"));
+
+         value = pstate->lexptr[0] & 0xff;
+         if (is_byte && value > 127)
+           error (_("Non-ASCII value in raw byte string"));
+         obstack_1grow (&obstack, value);
+
+         ++pstate->lexptr;
+       }
+      else if (pstate->lexptr[0] == '"')
+       {
+         /* Make sure to skip the quote.  */
+         ++pstate->lexptr;
+         break;
+       }
+      else if (pstate->lexptr[0] == '\\')
+       {
+         value = lex_escape (is_byte);
+
+         if (is_byte)
+           obstack_1grow (&obstack, value);
+         else
+           convert_between_encodings ("UTF-32", "UTF-8", (gdb_byte *) &value,
+                                      sizeof (value), sizeof (value),
+                                      &obstack, translit_none);
+       }
+      else if (pstate->lexptr[0] == '\0')
+       error (_("Unexpected EOF in string"));
+      else
+       {
+         value = pstate->lexptr[0] & 0xff;
+         if (is_byte && value > 127)
+           error (_("Non-ASCII value in byte string"));
+         obstack_1grow (&obstack, value);
+         ++pstate->lexptr;
+       }
+    }
+
+  current_string_val.length = obstack_object_size (&obstack);
+  current_string_val.ptr = (const char *) obstack_finish (&obstack);
+  return is_byte ? BYTESTRING : STRING;
+}
+
+/* Return true if STRING starts with whitespace followed by a digit.  */
+
+static bool
+space_then_number (const char *string)
+{
+  const char *p = string;
+
+  while (p[0] == ' ' || p[0] == '\t')
+    ++p;
+  if (p == string)
+    return false;
+
+  return *p >= '0' && *p <= '9';
+}
+
+/* Return true if C can start an identifier.  */
+
+static bool
+rust_identifier_start_p (char c)
+{
+  return ((c >= 'a' && c <= 'z')
+         || (c >= 'A' && c <= 'Z')
+         || c == '_'
+         || c == '$');
+}
+
+/* Lex an identifier.  */
+
+int
+rust_parser::lex_identifier ()
+{
+  const char *start = pstate->lexptr;
+  unsigned int length;
+  const struct token_info *token;
+  int i;
+  int is_gdb_var = pstate->lexptr[0] == '$';
+
+  gdb_assert (rust_identifier_start_p (pstate->lexptr[0]));
+
+  ++pstate->lexptr;
+
+  /* For the time being this doesn't handle Unicode rules.  Non-ASCII
+     identifiers are gated anyway.  */
+  while ((pstate->lexptr[0] >= 'a' && pstate->lexptr[0] <= 'z')
+        || (pstate->lexptr[0] >= 'A' && pstate->lexptr[0] <= 'Z')
+        || pstate->lexptr[0] == '_'
+        || (is_gdb_var && pstate->lexptr[0] == '$')
+        || (pstate->lexptr[0] >= '0' && pstate->lexptr[0] <= '9'))
+    ++pstate->lexptr;
+
+
+  length = pstate->lexptr - start;
+  token = NULL;
+  for (i = 0; i < ARRAY_SIZE (identifier_tokens); ++i)
+    {
+      if (length == strlen (identifier_tokens[i].name)
+         && strncmp (identifier_tokens[i].name, start, length) == 0)
+       {
+         token = &identifier_tokens[i];
+         break;
+       }
+    }
+
+  if (token != NULL)
+    {
+      if (token->value == 0)
+       {
+         /* Leave the terminating token alone.  */
+         pstate->lexptr = start;
+         return 0;
+       }
+    }
+  else if (token == NULL
+          && (strncmp (start, "thread", length) == 0
+              || strncmp (start, "task", length) == 0)
+          && space_then_number (pstate->lexptr))
+    {
+      /* "task" or "thread" followed by a number terminates the
+        parse, per gdb rules.  */
+      pstate->lexptr = start;
+      return 0;
+    }
+
+  if (token == NULL || (pstate->parse_completion && pstate->lexptr[0] == '\0'))
+    {
+      current_string_val.length = length;
+      current_string_val.ptr = start;
+    }
+
+  if (pstate->parse_completion && pstate->lexptr[0] == '\0')
+    {
+      /* Prevent rustyylex from returning two COMPLETE tokens.  */
+      pstate->prev_lexptr = pstate->lexptr;
+      return COMPLETE;
+    }
+
+  if (token != NULL)
+    return token->value;
+  if (is_gdb_var)
+    return GDBVAR;
+  return IDENT;
+}
+
+/* Lex an operator.  */
+
+int
+rust_parser::lex_operator ()
+{
+  const struct token_info *token = NULL;
+  int i;
+
+  for (i = 0; i < ARRAY_SIZE (operator_tokens); ++i)
+    {
+      if (strncmp (operator_tokens[i].name, pstate->lexptr,
+                  strlen (operator_tokens[i].name)) == 0)
+       {
+         pstate->lexptr += strlen (operator_tokens[i].name);
+         token = &operator_tokens[i];
+         break;
+       }
+    }
+
+  if (token != NULL)
+    {
+      current_opcode = token->opcode;
+      return token->value;
+    }
+
+  return *pstate->lexptr++;
+}
+
+/* Lex a number.  */
+
+int
+rust_parser::lex_number ()
+{
+  regmatch_t subexps[NUM_SUBEXPRESSIONS];
+  int match;
+  int is_integer = 0;
+  int could_be_decimal = 1;
+  int implicit_i32 = 0;
+  const char *type_name = NULL;
+  struct type *type;
+  int end_index;
+  int type_index = -1;
+  int i;
+
+  match = regexec (&number_regex, pstate->lexptr, ARRAY_SIZE (subexps),
+                  subexps, 0);
+  /* Failure means the regexp is broken.  */
+  gdb_assert (match == 0);
+
+  if (subexps[INT_TEXT].rm_so != -1)
+    {
+      /* Integer part matched.  */
+      is_integer = 1;
+      end_index = subexps[INT_TEXT].rm_eo;
+      if (subexps[INT_TYPE].rm_so == -1)
+       {
+         type_name = "i32";
+         implicit_i32 = 1;
+       }
+      else
+       {
+         type_index = INT_TYPE;
+         could_be_decimal = 0;
+       }
+    }
+  else if (subexps[FLOAT_TYPE1].rm_so != -1)
+    {
+      /* Found floating point type suffix.  */
+      end_index = subexps[FLOAT_TYPE1].rm_so;
+      type_index = FLOAT_TYPE1;
+    }
+  else if (subexps[FLOAT_TYPE2].rm_so != -1)
+    {
+      /* Found floating point type suffix.  */
+      end_index = subexps[FLOAT_TYPE2].rm_so;
+      type_index = FLOAT_TYPE2;
+    }
+  else
+    {
+      /* Any other floating point match.  */
+      end_index = subexps[0].rm_eo;
+      type_name = "f64";
+    }
+
+  /* We need a special case if the final character is ".".  In this
+     case we might need to parse an integer.  For example, "23.f()" is
+     a request for a trait method call, not a syntax error involving
+     the floating point number "23.".  */
+  gdb_assert (subexps[0].rm_eo > 0);
+  if (pstate->lexptr[subexps[0].rm_eo - 1] == '.')
+    {
+      const char *next = skip_spaces (&pstate->lexptr[subexps[0].rm_eo]);
+
+      if (rust_identifier_start_p (*next) || *next == '.')
+       {
+         --subexps[0].rm_eo;
+         is_integer = 1;
+         end_index = subexps[0].rm_eo;
+         type_name = "i32";
+         could_be_decimal = 1;
+         implicit_i32 = 1;
+       }
+    }
+
+  /* Compute the type name if we haven't already.  */
+  std::string type_name_holder;
+  if (type_name == NULL)
+    {
+      gdb_assert (type_index != -1);
+      type_name_holder = std::string ((pstate->lexptr
+                                      + subexps[type_index].rm_so),
+                                     (subexps[type_index].rm_eo
+                                      - subexps[type_index].rm_so));
+      type_name = type_name_holder.c_str ();
+    }
+
+  /* Look up the type.  */
+  type = get_type (type_name);
+
+  /* Copy the text of the number and remove the "_"s.  */
+  std::string number;
+  for (i = 0; i < end_index && pstate->lexptr[i]; ++i)
+    {
+      if (pstate->lexptr[i] == '_')
+       could_be_decimal = 0;
+      else
+       number.push_back (pstate->lexptr[i]);
+    }
+
+  /* Advance past the match.  */
+  pstate->lexptr += subexps[0].rm_eo;
+
+  /* Parse the number.  */
+  if (is_integer)
+    {
+      uint64_t value;
+      int radix = 10;
+      int offset = 0;
+
+      if (number[0] == '0')
+       {
+         if (number[1] == 'x')
+           radix = 16;
+         else if (number[1] == 'o')
+           radix = 8;
+         else if (number[1] == 'b')
+           radix = 2;
+         if (radix != 10)
+           {
+             offset = 2;
+             could_be_decimal = 0;
+           }
+       }
+
+      value = strtoulst (number.c_str () + offset, NULL, radix);
+      if (implicit_i32 && value >= ((uint64_t) 1) << 31)
+       type = get_type ("i64");
+
+      current_int_val.val = value;
+      current_int_val.type = type;
+    }
+  else
+    {
+      current_float_val.type = type;
+      bool parsed = parse_float (number.c_str (), number.length (),
+                                current_float_val.type,
+                                current_float_val.val.data ());
+      gdb_assert (parsed);
+    }
+
+  return is_integer ? (could_be_decimal ? DECIMAL_INTEGER : INTEGER) : FLOAT;
+}
+
+/* The lexer.  */
+
+int
+rust_parser::lex_one_token ()
+{
+  /* Skip all leading whitespace.  */
+  while (pstate->lexptr[0] == ' '
+        || pstate->lexptr[0] == '\t'
+        || pstate->lexptr[0] == '\r'
+        || pstate->lexptr[0] == '\n')
+    ++pstate->lexptr;
+
+  /* If we hit EOF and we're completing, then return COMPLETE -- maybe
+     we're completing an empty string at the end of a field_expr.
+     But, we don't want to return two COMPLETE tokens in a row.  */
+  if (pstate->lexptr[0] == '\0' && pstate->lexptr == pstate->prev_lexptr)
+    return 0;
+  pstate->prev_lexptr = pstate->lexptr;
+  if (pstate->lexptr[0] == '\0')
+    {
+      if (pstate->parse_completion)
+       {
+         current_string_val.length =0;
+         current_string_val.ptr = "";
+         return COMPLETE;
+       }
+      return 0;
+    }
+
+  if (pstate->lexptr[0] >= '0' && pstate->lexptr[0] <= '9')
+    return lex_number ();
+  else if (pstate->lexptr[0] == 'b' && pstate->lexptr[1] == '\'')
+    return lex_character ();
+  else if (pstate->lexptr[0] == 'b' && pstate->lexptr[1] == '"')
+    return lex_string ();
+  else if (pstate->lexptr[0] == 'b' && starts_raw_string (pstate->lexptr + 1))
+    return lex_string ();
+  else if (starts_raw_string (pstate->lexptr))
+    return lex_string ();
+  else if (rust_identifier_start_p (pstate->lexptr[0]))
+    return lex_identifier ();
+  else if (pstate->lexptr[0] == '"')
+    return lex_string ();
+  else if (pstate->lexptr[0] == '\'')
+    return lex_character ();
+  else if (pstate->lexptr[0] == '}' || pstate->lexptr[0] == ']')
+    {
+      /* Falls through to lex_operator.  */
+      --paren_depth;
+    }
+  else if (pstate->lexptr[0] == '(' || pstate->lexptr[0] == '{')
+    {
+      /* Falls through to lex_operator.  */
+      ++paren_depth;
+    }
+  else if (pstate->lexptr[0] == ',' && pstate->comma_terminates
+          && paren_depth == 0)
+    return 0;
+
+  return lex_operator ();
+}
+
+/* Push back a single character to be re-lexed.  */
+
+void
+rust_parser::push_back (char c)
+{
+  /* Can't be called before any lexing.  */
+  gdb_assert (pstate->prev_lexptr != NULL);
+
+  --pstate->lexptr;
+  gdb_assert (*pstate->lexptr == c);
+}
+
+\f
+
+/* Parse a tuple or paren expression.  */
+
+operation_up
+rust_parser::parse_tuple ()
+{
+  assume ('(');
+
+  if (current_token == ')')
+    {
+      lex ();
+      struct type *unit = get_type ("()");
+      return make_operation<long_const_operation> (unit, 0);
+    }
+
+  operation_up expr = parse_expr ();
+  if (current_token == ')')
+    {
+      /* Parenthesized expression.  */
+      lex ();
+      return expr;
+    }
+
+  std::vector<operation_up> ops;
+  ops.push_back (std::move (expr));
+  while (current_token != ')')
+    {
+      if (current_token != ',')
+       error (_("',' or ')' expected"));
+      lex ();
+
+      /* A trailing "," is ok.  */
+      if (current_token != ')')
+       ops.push_back (parse_expr ());
+    }
+
+  assume (')');
+
+  error (_("Tuple expressions not supported yet"));
+}
+
+/* Parse an array expression.  */
+
+operation_up
+rust_parser::parse_array ()
+{
+  assume ('[');
+
+  if (current_token == KW_MUT)
+    lex ();
+
+  operation_up result;
+  operation_up expr = parse_expr ();
+  if (current_token == ';')
+    {
+      lex ();
+      operation_up rhs = parse_expr ();
+      result = make_operation<rust_array_operation> (std::move (expr),
+                                                    std::move (rhs));
+    }
+  else if (current_token == ',')
+    {
+      std::vector<operation_up> ops;
+      ops.push_back (std::move (expr));
+      while (current_token != ']')
+       {
+         if (current_token != ',')
+           error (_("',' or ']' expected"));
+         lex ();
+         ops.push_back (parse_expr ());
+       }
+      ops.shrink_to_fit ();
+      int len = ops.size () - 1;
+      result = make_operation<array_operation> (0, len, std::move (ops));
+    }
+  else if (current_token != ']')
+    error (_("',', ';', or ']' expected"));
+
+  require (']');
+
+  return result;
+}
+
+/* Turn a name into an operation.  */
+
+operation_up
+rust_parser::name_to_operation (const std::string &name)
+{
+  struct block_symbol sym = lookup_symbol (name.c_str (),
+                                          pstate->expression_context_block,
+                                          VAR_DOMAIN);
+  if (sym.symbol != nullptr && SYMBOL_CLASS (sym.symbol) != LOC_TYPEDEF)
+    return make_operation<var_value_operation> (sym);
+
+  struct type *type = nullptr;
+
+  if (sym.symbol != nullptr)
+    {
+      gdb_assert (SYMBOL_CLASS (sym.symbol) == LOC_TYPEDEF);
+      type = SYMBOL_TYPE (sym.symbol);
+    }
+  if (type == nullptr)
+    type = rust_lookup_type (name.c_str ());
+  if (type == nullptr)
+    error (_("No symbol '%s' in current context"), name.c_str ());
+
+  if (type->code () == TYPE_CODE_STRUCT && type->num_fields () == 0)
+    {
+      /* A unit-like struct.  */
+      operation_up result (new rust_aggregate_operation (type, {}, {}));
+      return result;
+    }
+  else
+    return make_operation<type_operation> (type);
+}
+
+/* Parse a struct expression.  */
+
+operation_up
+rust_parser::parse_struct_expr (struct type *type)
+{
+  assume ('{');
+
+  if (type->code () != TYPE_CODE_STRUCT
+      || rust_tuple_type_p (type)
+      || rust_tuple_struct_type_p (type))
+    error (_("Struct expression applied to non-struct type"));
+
+  std::vector<std::pair<std::string, operation_up>> field_v;
+  while (current_token != '}' && current_token != DOTDOT)
+    {
+      if (current_token != IDENT)
+       error (_("'}', '..', or identifier expected"));
+
+      std::string name = get_string ();
+      lex ();
+
+      operation_up expr;
+      if (current_token == ',' || current_token == '}'
+         || current_token == DOTDOT)
+       expr = name_to_operation (name);
+      else
+       {
+         require (':');
+         expr = parse_expr ();
+       }
+      field_v.emplace_back (std::move (name), std::move (expr));
+
+      /* A trailing "," is ok.  */
+      if (current_token == ',')
+       lex ();
+    }
+
+  operation_up others;
+  if (current_token == DOTDOT)
+    {
+      lex ();
+      others = parse_expr ();
+    }
+
+  require ('}');
+
+  return make_operation<rust_aggregate_operation> (type,
+                                                  std::move (others),
+                                                  std::move (field_v));
+}
+
+/* Used by the operator precedence parser.  */
+struct rustop_item
+{
+  rustop_item (int token_, int precedence_, enum exp_opcode opcode_,
+              operation_up &&op_)
+    : token (token_),
+      precedence (precedence_),
+      opcode (opcode_),
+      op (std::move (op_))
+  {
+  }
+
+  /* The token value.  */
+  int token;
+  /* Precedence of this operator.  */
+  int precedence;
+  /* This is used only for assign-modify.  */
+  enum exp_opcode opcode;
+  /* The right hand side of this operation.  */
+  operation_up op;
+};
+
+/* An operator precedence parser for binary operations, including
+   "as".  */
+
+operation_up
+rust_parser::parse_binop (bool required)
+{
+  /* All the binary  operators.  Each one is of the form
+     OPERATION(TOKEN, PRECEDENCE, TYPE)
+     TOKEN is the corresponding operator token.
+     PRECEDENCE is a value indicating relative precedence.
+     TYPE is the operation type corresponding to the operator.
+     Assignment operations are handled specially, not via this
+     table; they have precedence 0.  */
+#define ALL_OPS                                        \
+  OPERATION ('*', 10, mul_operation)           \
+  OPERATION ('/', 10, div_operation)           \
+  OPERATION ('%', 10, rem_operation)           \
+  OPERATION ('@', 9, repeat_operation)         \
+  OPERATION ('+', 8, add_operation)            \
+  OPERATION ('-', 8, sub_operation)            \
+  OPERATION (LSH, 7, lsh_operation)            \
+  OPERATION (RSH, 7, rsh_operation)            \
+  OPERATION ('&', 6, bitwise_and_operation)    \
+  OPERATION ('^', 5, bitwise_xor_operation)    \
+  OPERATION ('|', 4, bitwise_ior_operation)    \
+  OPERATION (EQEQ, 3, equal_operation)         \
+  OPERATION (NOTEQ, 3, notequal_operation)     \
+  OPERATION ('<', 3, less_operation)           \
+  OPERATION (LTEQ, 3, leq_operation)           \
+  OPERATION ('>', 3, gtr_operation)            \
+  OPERATION (GTEQ, 3, geq_operation)           \
+  OPERATION (ANDAND, 2, logical_and_operation) \
+  OPERATION (OROR, 1, logical_or_operation)
+
+  operation_up start = parse_atom (required);
+  if (start == nullptr)
+    {
+      gdb_assert (!required);
+      return start;
+    }
+
+  std::vector<rustop_item> operator_stack;
+  operator_stack.emplace_back (0, -1, OP_NULL, std::move (start));
+
+  while (true)
+    {
+      int this_token = current_token;
+      enum exp_opcode compound_assign_op = OP_NULL;
+      int precedence = -2;
+
+      switch (this_token)
+       {
+#define OPERATION(TOKEN, PRECEDENCE, TYPE)             \
+         case TOKEN:                           \
+           precedence = PRECEDENCE;            \
+           lex ();                             \
+           break;
+
+         ALL_OPS
+
+#undef OPERATION
+
+       case COMPOUND_ASSIGN:
+         compound_assign_op = current_opcode;
+         /* FALLTHROUGH */
+       case '=':
+         precedence = 0;
+         lex ();
+         break;
+
+         /* "as" must be handled specially.  */
+       case KW_AS:
+         {
+           lex ();
+           rustop_item &lhs = operator_stack.back ();
+           struct type *type = parse_type ();
+           lhs.op = make_operation<unop_cast_operation> (std::move (lhs.op),
+                                                         type);
+         }
+         /* Bypass the rest of the loop.  */
+         continue;
+
+       default:
+         /* Arrange to pop the entire stack.  */
+         precedence = -2;
+         break;
+        }
+
+      while (precedence < operator_stack.back ().precedence
+            && operator_stack.size () > 1)
+       {
+         rustop_item rhs = std::move (operator_stack.back ());
+         operator_stack.pop_back ();
+
+         rustop_item &lhs = operator_stack.back ();
+
+         switch (rhs.token)
+           {
+#define OPERATION(TOKEN, PRECEDENCE, TYPE)                     \
+         case TOKEN:                                           \
+           lhs.op = make_operation<TYPE> (std::move (lhs.op),  \
+                                          std::move (rhs.op)); \
+           break;
+
+             ALL_OPS
+
+#undef OPERATION
+
+           case '=':
+           case COMPOUND_ASSIGN:
+             {
+               if (rhs.token == '=')
+                 lhs.op = (make_operation<assign_operation>
+                           (std::move (lhs.op), std::move (rhs.op)));
+               else
+                 lhs.op = (make_operation<assign_modify_operation>
+                           (rhs.opcode, std::move (lhs.op),
+                            std::move (rhs.op)));
+
+               struct type *unit_type = get_type ("()");
+
+               operation_up nil (new long_const_operation (unit_type, 0));
+               lhs.op = (make_operation<comma_operation>
+                         (std::move (lhs.op), std::move (nil)));
+             }
+             break;
+
+           default:
+             gdb_assert_not_reached ("bad binary operator");
+           }
+       }
+
+      if (precedence == -2)
+       break;
+
+      operator_stack.emplace_back (this_token, precedence, compound_assign_op,
+                                  parse_atom (true));
+    }
+
+  gdb_assert (operator_stack.size () == 1);
+  return std::move (operator_stack[0].op);
+#undef ALL_OPS
+}
+
+/* Parse a range expression.  */
+
+operation_up
+rust_parser::parse_range ()
+{
+  enum range_flag kind = (RANGE_HIGH_BOUND_DEFAULT
+                         | RANGE_LOW_BOUND_DEFAULT);
+
+  operation_up lhs;
+  if (current_token != DOTDOT && current_token != DOTDOTEQ)
+    {
+      lhs = parse_binop (true);
+      kind &= ~RANGE_LOW_BOUND_DEFAULT;
+    }
+
+  if (current_token == DOTDOT)
+    kind |= RANGE_HIGH_BOUND_EXCLUSIVE;
+  else if (current_token != DOTDOTEQ)
+    return lhs;
+  lex ();
+
+  /* A "..=" range requires a high bound, but otherwise it is
+     optional.  */
+  operation_up rhs = parse_binop ((kind & RANGE_HIGH_BOUND_EXCLUSIVE) == 0);
+  if (rhs != nullptr)
+    kind &= ~RANGE_HIGH_BOUND_DEFAULT;
+
+  return make_operation<rust_range_operation> (kind,
+                                              std::move (lhs),
+                                              std::move (rhs));
+}
+
+/* Parse an expression.  */
+
+operation_up
+rust_parser::parse_expr ()
+{
+  return parse_range ();
+}
+
+/* Parse a sizeof expression.  */
+
+operation_up
+rust_parser::parse_sizeof ()
+{
+  assume (KW_SIZEOF);
+
+  if (current_token == KW_MUT)
+    lex ();
+
+  require ('(');
+  operation_up result = make_operation<unop_sizeof_operation> (parse_expr ());
+  require (')');
+  return result;
+}
+
+/* Parse an address-of operation.  */
+
+operation_up
+rust_parser::parse_addr ()
+{
+  assume ('&');
+
+  if (current_token == KW_MUT)
+    lex ();
+
+  return make_operation<rust_unop_addr_operation> (parse_atom (true));
+}
+
+/* Parse a field expression.  */
+
+operation_up
+rust_parser::parse_field (operation_up &&lhs)
+{
+  assume ('.');
+
+  operation_up result;
+  switch (current_token)
+    {
+    case IDENT:
+    case COMPLETE:
+      {
+       bool is_complete = current_token == COMPLETE;
+       auto struct_op = new rust_structop (std::move (lhs), get_string ());
+       lex ();
+       if (is_complete)
+         {
+           completion_op.reset (struct_op);
+           pstate->mark_struct_expression (struct_op);
+           /* Throw to the outermost level of the parser.  */
+           error (_("not really an error"));
+         }
+       result.reset (struct_op);
+      }
+      break;
+
+    case DECIMAL_INTEGER:
+      result = make_operation<rust_struct_anon> (current_int_val.val,
+                                                std::move (lhs));
+      lex ();
+      break;
+
+    case INTEGER:
+      error (_("'_' not allowed in integers in anonymous field references"));
+
+    default:
+      error (_("field name expected"));
+    }
+
+  return result;
+}
+
+/* Parse an index expression.  */
+
+operation_up
+rust_parser::parse_index (operation_up &&lhs)
+{
+  assume ('[');
+  operation_up rhs = parse_expr ();
+  require (']');
+
+  return make_operation<rust_subscript_operation> (std::move (lhs),
+                                                  std::move (rhs));
+}
+
+/* Parse a sequence of comma-separated expressions in parens.  */
+
+std::vector<operation_up>
+rust_parser::parse_paren_args ()
+{
+  assume ('(');
+
+  std::vector<operation_up> args;
+  while (current_token != ')')
+    {
+      if (!args.empty ())
+       {
+         if (current_token != ',')
+           error (_("',' or ')' expected"));
+         lex ();
+       }
+
+      args.push_back (parse_expr ());
+    }
+
+  assume (')');
+
+  return args;
+}
+
+/* Parse the parenthesized part of a function call.  */
+
+operation_up
+rust_parser::parse_call (operation_up &&lhs)
+{
+  std::vector<operation_up> args = parse_paren_args ();
+
+  return make_operation<funcall_operation> (std::move (lhs),
+                                           std::move (args));
+}
+
+/* Parse a list of types.  */
+
+std::vector<struct type *>
+rust_parser::parse_type_list ()
+{
+  std::vector<struct type *> result;
+  result.push_back (parse_type ());
+  while (current_token == ',')
+    {
+      lex ();
+      result.push_back (parse_type ());
+    }
+  return result;
+}
+
+/* Parse a possibly-empty list of types, surrounded in parens.  */
+
+std::vector<struct type *>
+rust_parser::parse_maybe_type_list ()
+{
+  assume ('(');
+  std::vector<struct type *> types;
+  if (current_token != ')')
+    types = parse_type_list ();
+  require (')');
+  return types;
+}
+
+/* Parse an array type.  */
+
+struct type *
+rust_parser::parse_array_type ()
+{
+  assume ('[');
+  struct type *elt_type = parse_type ();
+  require (';');
+
+  if (current_token != INTEGER && current_token != DECIMAL_INTEGER)
+    error (_("integer expected"));
+  LONGEST val = current_int_val.val;
+  if (val < 0)
+    error (_("Negative array length"));
+  lex ();
+  require (']');
+
+  return lookup_array_range_type (elt_type, 0, val - 1);
+}
+
+/* Parse a slice type.  */
+
+struct type *
+rust_parser::parse_slice_type ()
+{
+  assume ('&');
+
+  bool is_slice = current_token == '[';
+  if (is_slice)
+    lex ();
+
+  struct type *target = parse_type ();
+
+  if (is_slice)
+    {
+      require (']');
+      return rust_slice_type ("&[*gdb*]", target, get_type ("usize"));
+    }
+
+  /* For now we treat &x and *x identically.  */
+  return lookup_pointer_type (target);
+}
+
+/* Parse a pointer type.  */
+
+struct type *
+rust_parser::parse_pointer_type ()
+{
+  assume ('*');
+
+  if (current_token == KW_MUT || current_token == KW_CONST)
+    lex ();
+
+  struct type *target = parse_type ();
+  /* For the time being we ignore mut/const.  */
+  return lookup_pointer_type (target);
+}
+
+/* Parse a function type.  */
+
+struct type *
+rust_parser::parse_function_type ()
+{
+  assume (KW_FN);
+
+  if (current_token != '(')
+    error (_("'(' expected"));
+
+  std::vector<struct type *> types = parse_maybe_type_list ();
+
+  if (current_token != ARROW)
+    error (_("'->' expected"));
+  lex ();
+
+  struct type *result_type = parse_type ();
+
+  struct type **argtypes = nullptr;
+  if (!types.empty ())
+    argtypes = types.data ();
+
+  result_type = lookup_function_type_with_arguments (result_type,
+                                                    types.size (),
+                                                    argtypes);
+  return lookup_pointer_type (result_type);
+}
+
+/* Parse a tuple type.  */
+
+struct type *
+rust_parser::parse_tuple_type ()
+{
+  std::vector<struct type *> types = parse_maybe_type_list ();
+
+  auto_obstack obstack;
+  obstack_1grow (&obstack, '(');
+  for (int i = 0; i < types.size (); ++i)
+    {
+      std::string type_name = type_to_string (types[i]);
+
+      if (i > 0)
+       obstack_1grow (&obstack, ',');
+      obstack_grow_str (&obstack, type_name.c_str ());
+    }
+
+  obstack_grow_str0 (&obstack, ")");
+  const char *name = (const char *) obstack_finish (&obstack);
+
+  /* We don't allow creating new tuple types (yet), but we do allow
+     looking up existing tuple types.  */
+  struct type *result = rust_lookup_type (name);
+  if (result == nullptr)
+    error (_("could not find tuple type '%s'"), name);
+
+  return result;
+}
+
+/* Parse a type.  */
+
+struct type *
+rust_parser::parse_type ()
+{
+  switch (current_token)
+    {
+    case '[':
+      return parse_array_type ();
+    case '&':
+      return parse_slice_type ();
+    case '*':
+      return parse_pointer_type ();
+    case KW_FN:
+      return parse_function_type ();
+    case '(':
+      return parse_tuple_type ();
+    case KW_SELF:
+    case KW_SUPER:
+    case COLONCOLON:
+    case KW_EXTERN:
+    case IDENT:
+      {
+       std::string path = parse_path (false);
+       struct type *result = rust_lookup_type (path.c_str ());
+       if (result == nullptr)
+         error (_("No type name '%s' in current context"), path.c_str ());
+       return result;
+      }
+    default:
+      error (_("type expected"));
+    }
+}
+
+/* Parse a path.  */
+
+std::string
+rust_parser::parse_path (bool for_expr)
+{
+  unsigned n_supers = 0;
+  int first_token = current_token;
+
+  switch (current_token)
+    {
+    case KW_SELF:
+      lex ();
+      if (current_token != COLONCOLON)
+       return "self";
+      lex ();
+      /* FALLTHROUGH */
+    case KW_SUPER:
+      while (current_token == KW_SUPER)
+       {
+         ++n_supers;
+         lex ();
+         if (current_token != COLONCOLON)
+           error (_("'::' expected"));
+         lex ();
+       }
+      break;
+
+    case COLONCOLON:
+      lex ();
+      break;
+
+    case KW_EXTERN:
+      /* This is a gdb extension to make it possible to refer to items
+        in other crates.  It just bypasses adding the current crate
+        to the front of the name.  */
+      lex ();
+      break;
+    }
+
+  if (current_token != IDENT)
+    error (_("identifier expected"));
+  std::string path = get_string ();
+  bool saw_ident = true;
+  lex ();
+
+  /* The condition here lets us enter the loop even if we see
+     "ident<...>".  */
+  while (current_token == COLONCOLON || current_token == '<')
+    {
+      if (current_token == COLONCOLON)
+       {
+         lex ();
+         saw_ident = false;
+
+         if (current_token == IDENT)
+           {
+             path = path + "::" + get_string ();
+             lex ();
+             saw_ident = true;
+           }
+         else if (current_token == COLONCOLON)
+           {
+             /* The code below won't detect this scenario.  */
+             error (_("unexpected '::'"));
+           }
+       }
+
+      if (current_token != '<')
+       continue;
+
+      /* Expression use name::<...>, whereas types use name<...>.  */
+      if (for_expr)
+       {
+         /* Expressions use "name::<...>", so if we saw an identifier
+            after the "::", we ignore the "<" here.  */
+         if (saw_ident)
+           break;
+       }
+      else
+       {
+         /* Types use "name<...>", so we need to have seen the
+            identifier.  */
+         if (!saw_ident)
+           break;
+       }
+
+      lex ();
+      std::vector<struct type *> types = parse_type_list ();
+      if (current_token == '>')
+       lex ();
+      else if (current_token == RSH)
+       {
+         push_back ('>');
+         lex ();
+       }
+      else
+       error (_("'>' expected"));
+
+      path += "<";
+      for (int i = 0; i < types.size (); ++i)
+       {
+         if (i > 0)
+           path += ",";
+         path += type_to_string (types[i]);
+       }
+      path += ">";
+      break;
+    }
+
+  switch (first_token)
+    {
+    case KW_SELF:
+    case KW_SUPER:
+      return super_name (path, n_supers);
+
+    case COLONCOLON:
+      return crate_name (path);
+
+    case KW_EXTERN:
+      return "::" + path;
+
+    case IDENT:
+      return path;
+
+    default:
+      gdb_assert_not_reached ("missing case in path parsing");
+    }
+}
+
+/* Handle the parsing for a string expression.  */
+
+operation_up
+rust_parser::parse_string ()
+{
+  gdb_assert (current_token == STRING);
+
+  /* Wrap the raw string in the &str struct.  */
+  struct type *type = rust_lookup_type ("&str");
+  if (type == nullptr)
+    error (_("Could not find type '&str'"));
+
+  std::vector<std::pair<std::string, operation_up>> field_v;
+
+  size_t len = current_string_val.length;
+  operation_up str = make_operation<string_operation> (get_string ());
+  operation_up addr
+    = make_operation<rust_unop_addr_operation> (std::move (str));
+  field_v.emplace_back ("data_ptr", std::move (addr));
+
+  struct type *valtype = get_type ("usize");
+  operation_up lenop = make_operation<long_const_operation> (valtype, len);
+  field_v.emplace_back ("length", std::move (lenop));
+
+  return make_operation<rust_aggregate_operation> (type,
+                                                  operation_up (),
+                                                  std::move (field_v));
+}
+
+/* Parse a tuple struct expression.  */
+
+operation_up
+rust_parser::parse_tuple_struct (struct type *type)
+{
+  std::vector<operation_up> args = parse_paren_args ();
+
+  std::vector<std::pair<std::string, operation_up>> field_v (args.size ());
+  for (int i = 0; i < args.size (); ++i)
+    field_v[i] = { string_printf ("__%d", i), std::move (args[i]) };
+
+  return (make_operation<rust_aggregate_operation>
+         (type, operation_up (), std::move (field_v)));
+}
+
+/* Parse a path expression.  */
+
+operation_up
+rust_parser::parse_path_expr ()
+{
+  std::string path = parse_path (true);
+
+  if (current_token == '{')
+    {
+      struct type *type = rust_lookup_type (path.c_str ());
+      if (type == nullptr)
+       error (_("Could not find type '%s'"), path.c_str ());
+      
+      return parse_struct_expr (type);
+    }
+  else if (current_token == '(')
+    {
+      struct type *type = rust_lookup_type (path.c_str ());
+      /* If this is actually a tuple struct expression, handle it
+        here.  If it is a call, it will be handled elsewhere.  */
+      if (type != nullptr)
+       {
+         if (!rust_tuple_struct_type_p (type))
+           error (_("Type %s is not a tuple struct"), path.c_str ());
+         return parse_tuple_struct (type);
+       }
+    }
+
+  return name_to_operation (path);
+}
+
+/* Parse an atom.  "Atom" isn't a Rust term, but this refers to a
+   single unitary item in the grammar; but here including some unary
+   prefix and postfix expressions.  */
+
+operation_up
+rust_parser::parse_atom (bool required)
+{
+  operation_up result;
+
+  switch (current_token)
+    {
+    case '(':
+      result = parse_tuple ();
+      break;
+
+    case '[':
+      result = parse_array ();
+      break;
+
+    case INTEGER:
+    case DECIMAL_INTEGER:
+      result = make_operation<long_const_operation> (current_int_val.type,
+                                                    current_int_val.val);
+      lex ();
+      break;
+
+    case FLOAT:
+      result = make_operation<float_const_operation> (current_float_val.type,
+                                                     current_float_val.val);
+      lex ();
+      break;
+
+    case STRING:
+      result = parse_string ();
+      break;
+
+    case BYTESTRING:
+      result = make_operation<string_operation> (get_string ());
+      lex ();
+      break;
+
+    case KW_TRUE:
+    case KW_FALSE:
+      result = make_operation<bool_operation> (current_token == KW_TRUE);
+      lex ();
+      break;
+
+    case GDBVAR:
+      /* This is kind of a hacky approach.  */
+      {
+       pstate->push_dollar (current_string_val);
+       result = pstate->pop ();
+       lex ();
+      }
+      break;
+
+    case KW_SELF:
+    case KW_SUPER:
+    case COLONCOLON:
+    case KW_EXTERN:
+    case IDENT:
+      result = parse_path_expr ();
+      break;
+
+    case '*':
+      lex ();
+      result = make_operation<rust_unop_ind_operation> (parse_atom (true));
+      break;
+    case '+':
+      lex ();
+      result = make_operation<unary_plus_operation> (parse_atom (true));
+      break;
+    case '-':
+      lex ();
+      result = make_operation<unary_neg_operation> (parse_atom (true));
+      break;
+    case '!':
+      lex ();
+      result = make_operation<rust_unop_compl_operation> (parse_atom (true));
+      break;
+    case KW_SIZEOF:
+      result = parse_sizeof ();
+      break;
+    case '&':
+      result = parse_addr ();
+      break;
+
+    default:
+      if (!required)
+       return {};
+      error (_("unexpected token"));
+    }
+
+  /* Now parse suffixes.  */
+  while (true)
+    {
+      switch (current_token)
+       {
+       case '.':
+         result = parse_field (std::move (result));
+         break;
+
+       case '[':
+         result = parse_index (std::move (result));
+         break;
+
+       case '(':
+         result = parse_call (std::move (result));
+         break;
+
+       default:
+         return result;
+       }
+    }
+}
+
+\f
+
+/* The parser as exposed to gdb.  */
+
+int
+rust_language::parser (struct parser_state *state) const
+{
+  rust_parser parser (state);
+
+  operation_up result;
+  try
+    {
+      result = parser.parse_entry_point ();
+    }
+  catch (const gdb_exception &exc)
+    {
+      if (state->parse_completion)
+       {
+         result = std::move (parser.completion_op);
+         if (result == nullptr)
+           throw;
+       }
+      else
+       throw;
+    }
+
+  state->set_operation (std::move (result));
+
+  return 0;
+}
+
+\f
+
+#if GDB_SELF_TEST
+
+/* A test helper that lexes a string, expecting a single token.  */
+
+static void
+rust_lex_test_one (rust_parser *parser, const char *input, int expected)
+{
+  int token;
+
+  parser->reset (input);
+
+  token = parser->lex_one_token ();
+  SELF_CHECK (token == expected);
+
+  if (token)
+    {
+      token = parser->lex_one_token ();
+      SELF_CHECK (token == 0);
+    }
+}
+
+/* Test that INPUT lexes as the integer VALUE.  */
+
+static void
+rust_lex_int_test (rust_parser *parser, const char *input,
+                  LONGEST value, int kind)
+{
+  rust_lex_test_one (parser, input, kind);
+  SELF_CHECK (parser->current_int_val.val == value);
+}
+
+/* Test that INPUT throws an exception with text ERR.  */
+
+static void
+rust_lex_exception_test (rust_parser *parser, const char *input,
+                        const char *err)
+{
+  try
+    {
+      /* The "kind" doesn't matter.  */
+      rust_lex_test_one (parser, input, DECIMAL_INTEGER);
+      SELF_CHECK (0);
+    }
+  catch (const gdb_exception_error &except)
+    {
+      SELF_CHECK (strcmp (except.what (), err) == 0);
+    }
+}
+
+/* Test that INPUT lexes as the identifier, string, or byte-string
+   VALUE.  KIND holds the expected token kind.  */
+
+static void
+rust_lex_stringish_test (rust_parser *parser, const char *input,
+                        const char *value, int kind)
+{
+  rust_lex_test_one (parser, input, kind);
+  SELF_CHECK (parser->get_string () == value);
+}
+
+/* Helper to test that a string parses as a given token sequence.  */
+
+static void
+rust_lex_test_sequence (rust_parser *parser, const char *input, int len,
+                       const int expected[])
+{
+  int i;
+
+  parser->reset (input);
+
+  for (i = 0; i < len; ++i)
+    {
+      int token = parser->lex_one_token ();
+      SELF_CHECK (token == expected[i]);
+    }
+}
+
+/* Tests for an integer-parsing corner case.  */
+
+static void
+rust_lex_test_trailing_dot (rust_parser *parser)
+{
+  const int expected1[] = { DECIMAL_INTEGER, '.', IDENT, '(', ')', 0 };
+  const int expected2[] = { INTEGER, '.', IDENT, '(', ')', 0 };
+  const int expected3[] = { FLOAT, EQEQ, '(', ')', 0 };
+  const int expected4[] = { DECIMAL_INTEGER, DOTDOT, DECIMAL_INTEGER, 0 };
+
+  rust_lex_test_sequence (parser, "23.g()", ARRAY_SIZE (expected1), expected1);
+  rust_lex_test_sequence (parser, "23_0.g()", ARRAY_SIZE (expected2),
+                         expected2);
+  rust_lex_test_sequence (parser, "23.==()", ARRAY_SIZE (expected3),
+                         expected3);
+  rust_lex_test_sequence (parser, "23..25", ARRAY_SIZE (expected4), expected4);
+}
+
+/* Tests of completion.  */
+
+static void
+rust_lex_test_completion (rust_parser *parser)
+{
+  const int expected[] = { IDENT, '.', COMPLETE, 0 };
+
+  parser->pstate->parse_completion = 1;
+
+  rust_lex_test_sequence (parser, "something.wha", ARRAY_SIZE (expected),
+                         expected);
+  rust_lex_test_sequence (parser, "something.", ARRAY_SIZE (expected),
+                         expected);
+
+  parser->pstate->parse_completion = 0;
+}
+
+/* Test pushback.  */
+
+static void
+rust_lex_test_push_back (rust_parser *parser)
+{
+  int token;
+
+  parser->reset (">>=");
+
+  token = parser->lex_one_token ();
+  SELF_CHECK (token == COMPOUND_ASSIGN);
+  SELF_CHECK (parser->current_opcode == BINOP_RSH);
+
+  parser->push_back ('=');
+
+  token = parser->lex_one_token ();
+  SELF_CHECK (token == '=');
+
+  token = parser->lex_one_token ();
+  SELF_CHECK (token == 0);
+}
+
+/* Unit test the lexer.  */
+
+static void
+rust_lex_tests (void)
+{
+  int i;
+
+  /* Set up dummy "parser", so that rust_type works.  */
+  struct parser_state ps (language_def (language_rust), target_gdbarch (),
+                         nullptr, 0, 0, nullptr, 0, nullptr, false);
+  rust_parser parser (&ps);
+
+  rust_lex_test_one (&parser, "", 0);
+  rust_lex_test_one (&parser, "    \t  \n \r  ", 0);
+  rust_lex_test_one (&parser, "thread 23", 0);
+  rust_lex_test_one (&parser, "task 23", 0);
+  rust_lex_test_one (&parser, "th 104", 0);
+  rust_lex_test_one (&parser, "ta 97", 0);
+
+  rust_lex_int_test (&parser, "'z'", 'z', INTEGER);
+  rust_lex_int_test (&parser, "'\\xff'", 0xff, INTEGER);
+  rust_lex_int_test (&parser, "'\\u{1016f}'", 0x1016f, INTEGER);
+  rust_lex_int_test (&parser, "b'z'", 'z', INTEGER);
+  rust_lex_int_test (&parser, "b'\\xfe'", 0xfe, INTEGER);
+  rust_lex_int_test (&parser, "b'\\xFE'", 0xfe, INTEGER);
+  rust_lex_int_test (&parser, "b'\\xfE'", 0xfe, INTEGER);
+
+  /* Test all escapes in both modes.  */
+  rust_lex_int_test (&parser, "'\\n'", '\n', INTEGER);
+  rust_lex_int_test (&parser, "'\\r'", '\r', INTEGER);
+  rust_lex_int_test (&parser, "'\\t'", '\t', INTEGER);
+  rust_lex_int_test (&parser, "'\\\\'", '\\', INTEGER);
+  rust_lex_int_test (&parser, "'\\0'", '\0', INTEGER);
+  rust_lex_int_test (&parser, "'\\''", '\'', INTEGER);
+  rust_lex_int_test (&parser, "'\\\"'", '"', INTEGER);
+
+  rust_lex_int_test (&parser, "b'\\n'", '\n', INTEGER);
+  rust_lex_int_test (&parser, "b'\\r'", '\r', INTEGER);
+  rust_lex_int_test (&parser, "b'\\t'", '\t', INTEGER);
+  rust_lex_int_test (&parser, "b'\\\\'", '\\', INTEGER);
+  rust_lex_int_test (&parser, "b'\\0'", '\0', INTEGER);
+  rust_lex_int_test (&parser, "b'\\''", '\'', INTEGER);
+  rust_lex_int_test (&parser, "b'\\\"'", '"', INTEGER);
+
+  rust_lex_exception_test (&parser, "'z", "Unterminated character literal");
+  rust_lex_exception_test (&parser, "b'\\x0'", "Not enough hex digits seen");
+  rust_lex_exception_test (&parser, "b'\\u{0}'",
+                          "Unicode escape in byte literal");
+  rust_lex_exception_test (&parser, "'\\x0'", "Not enough hex digits seen");
+  rust_lex_exception_test (&parser, "'\\u0'", "Missing '{' in Unicode escape");
+  rust_lex_exception_test (&parser, "'\\u{0", "Missing '}' in Unicode escape");
+  rust_lex_exception_test (&parser, "'\\u{0000007}", "Overlong hex escape");
+  rust_lex_exception_test (&parser, "'\\u{}", "Not enough hex digits seen");
+  rust_lex_exception_test (&parser, "'\\Q'", "Invalid escape \\Q in literal");
+  rust_lex_exception_test (&parser, "b'\\Q'", "Invalid escape \\Q in literal");
+
+  rust_lex_int_test (&parser, "23", 23, DECIMAL_INTEGER);
+  rust_lex_int_test (&parser, "2_344__29", 234429, INTEGER);
+  rust_lex_int_test (&parser, "0x1f", 0x1f, INTEGER);
+  rust_lex_int_test (&parser, "23usize", 23, INTEGER);
+  rust_lex_int_test (&parser, "23i32", 23, INTEGER);
+  rust_lex_int_test (&parser, "0x1_f", 0x1f, INTEGER);
+  rust_lex_int_test (&parser, "0b1_101011__", 0x6b, INTEGER);
+  rust_lex_int_test (&parser, "0o001177i64", 639, INTEGER);
+  rust_lex_int_test (&parser, "0x123456789u64", 0x123456789ull, INTEGER);
+
+  rust_lex_test_trailing_dot (&parser);
+
+  rust_lex_test_one (&parser, "23.", FLOAT);
+  rust_lex_test_one (&parser, "23.99f32", FLOAT);
+  rust_lex_test_one (&parser, "23e7", FLOAT);
+  rust_lex_test_one (&parser, "23E-7", FLOAT);
+  rust_lex_test_one (&parser, "23e+7", FLOAT);
+  rust_lex_test_one (&parser, "23.99e+7f64", FLOAT);
+  rust_lex_test_one (&parser, "23.82f32", FLOAT);
+
+  rust_lex_stringish_test (&parser, "hibob", "hibob", IDENT);
+  rust_lex_stringish_test (&parser, "hibob__93", "hibob__93", IDENT);
+  rust_lex_stringish_test (&parser, "thread", "thread", IDENT);
+
+  rust_lex_stringish_test (&parser, "\"string\"", "string", STRING);
+  rust_lex_stringish_test (&parser, "\"str\\ting\"", "str\ting", STRING);
+  rust_lex_stringish_test (&parser, "\"str\\\"ing\"", "str\"ing", STRING);
+  rust_lex_stringish_test (&parser, "r\"str\\ing\"", "str\\ing", STRING);
+  rust_lex_stringish_test (&parser, "r#\"str\\ting\"#", "str\\ting", STRING);
+  rust_lex_stringish_test (&parser, "r###\"str\\\"ing\"###", "str\\\"ing",
+                          STRING);
+
+  rust_lex_stringish_test (&parser, "b\"string\"", "string", BYTESTRING);
+  rust_lex_stringish_test (&parser, "b\"\x73tring\"", "string", BYTESTRING);
+  rust_lex_stringish_test (&parser, "b\"str\\\"ing\"", "str\"ing", BYTESTRING);
+  rust_lex_stringish_test (&parser, "br####\"\\x73tring\"####", "\\x73tring",
+                          BYTESTRING);
+
+  for (i = 0; i < ARRAY_SIZE (identifier_tokens); ++i)
+    rust_lex_test_one (&parser, identifier_tokens[i].name,
+                      identifier_tokens[i].value);
+
+  for (i = 0; i < ARRAY_SIZE (operator_tokens); ++i)
+    rust_lex_test_one (&parser, operator_tokens[i].name,
+                      operator_tokens[i].value);
+
+  rust_lex_test_completion (&parser);
+  rust_lex_test_push_back (&parser);
+}
+
+#endif /* GDB_SELF_TEST */
+
+\f
+
+void _initialize_rust_exp ();
+void
+_initialize_rust_exp ()
+{
+  int code = regcomp (&number_regex, number_regex_text, REG_EXTENDED);
+  /* If the regular expression was incorrect, it was a programming
+     error.  */
+  gdb_assert (code == 0);
+
+#if GDB_SELF_TEST
+  selftests::register_test ("rust-lex", rust_lex_tests);
+#endif
+}
index d659ec4ed1a99c00d185e44b93a0373448cdeb4e..995bb783bee815031a9d727fae507c8873ee482c 100644 (file)
@@ -1,3 +1,8 @@
+2021-04-16  Tom Tromey  <tom@tromey.com>
+
+       * gdb.rust/simple.exp: Change error text.
+       * gdb.rust/expr.exp: Change error text.
+
 2021-04-16  Tom Tromey  <tom@tromey.com>
 
        * gdb.rust/simple.exp: Add parens to 'as' test.
index d77c780d9cecfd0552b48aece0d3806025bb0990..d81b6fcbf57b022669cc83abb2cceb816c992b4a 100644 (file)
@@ -136,6 +136,10 @@ gdb_test "print \[mut 23usize; 4\]" " = \\\[23, 23, 23, 23\\\]"
 # Test lexer corner cases.
 gdb_test "print 0x0 as *mut ()" " = \\\(\\*mut \\\(\\\)\\\) 0x0"
 gdb_test "print 0x0 as fn(i64) -> ()" " = \\\(\\*mut fn \\\(i64\\\) -> \\\(\\\)\\\) 0x0"
-gdb_test "print r#" "syntax error in expression, near `#'\\."
+
+# The lexer doesn't treat this as a failure, but rather as two tokens,
+# and we error out while trying to look up 'r'.  This is fine, though
+# -- what's important is that it isn't accepted.
+gdb_test "print r#" "No symbol 'r' in current context"
 
 gdb_test "printf \"%d %d\\n\", 23+1, 23-1" "24 22"
index 6d5cd319f41056391df7b0d56d6dd35fb1355b6a..0be0e94ac4c71c1c6f9f0a5dd7893f092c314d27 100644 (file)
@@ -165,7 +165,7 @@ gdb_test "print simple::HiBob(0xff, 5)" \
     "Type simple::HiBob is not a tuple struct"
 gdb_test "print sizeof(simple::HiBob)" " = \[0-9\]+"
 gdb_test "print simple::HiBob + 5" \
-    "Found type 'simple::HiBob', which can't be evaluated in this context"
+    "Attempt to use a type name as an expression"
 gdb_test "print nosuchsymbol" \
     "No symbol 'nosuchsymbol' in current context"