Add operation-related methods to parser_state
authorTom Tromey <tom@tromey.com>
Mon, 8 Mar 2021 14:27:57 +0000 (07:27 -0700)
committerTom Tromey <tom@tromey.com>
Mon, 8 Mar 2021 14:28:37 +0000 (07:28 -0700)
This adds several operation-related methods to parser_state.  These
methods make it more convenient to change the parsers to be
operation-based.

Because byacc has poor support for C++, a stack of operations is added
to parser_state.  A parser can push operations, then later pop them
for combination into new operations.  This approach avoids the memory
leaks that would result if raw pointers were used in the parsers, at
the cost of parser productions not being type-safe (they can't
indicate that they return an operation).

This also introduces analogs of some write_exp functions, like
write_exp_string_vector, write_dollar_variable, and
write_exp_symbol_reference.

gdb/ChangeLog
2021-03-08  Tom Tromey  <tom@tromey.com>

* parser-defs.h (struct parser_state) <push, push_new,
push_c_string, push_symbol, push_dollar, pop, pop_vector, wrap,
wrap2>: New methods.
<m_operations>: New member.
* parse.c (parser_state::push_c_string)
(parser_state::push_symbol, parser_state::push_dollar): New
methods.

gdb/ChangeLog
gdb/parse.c
gdb/parser-defs.h

index 3436e4e83fc2bc56f1743f6c86a1ad2370dc5054..060b9a12b359637d9bf7a929e5741f3dcce5d646 100644 (file)
@@ -1,3 +1,13 @@
+2021-03-08  Tom Tromey  <tom@tromey.com>
+
+       * parser-defs.h (struct parser_state) <push, push_new,
+       push_c_string, push_symbol, push_dollar, pop, pop_vector, wrap,
+       wrap2>: New methods.
+       <m_operations>: New member.
+       * parse.c (parser_state::push_c_string)
+       (parser_state::push_symbol, parser_state::push_dollar): New
+       methods.
+
 2021-03-08  Tom Tromey  <tom@tromey.com>
 
        * parser-defs.h (struct expr_completion_state) <expout_last_op>:
index 79b1ca1612127beb485d7efaca299be368dc40c4..d6f98bf33cf1441fed75ee5b1999827383bbc98e 100644 (file)
@@ -545,6 +545,133 @@ parser_state::mark_completion_tag (enum type_code tag, const char *ptr,
   m_completion_state.expout_completion_name.reset (xstrndup (ptr, length));
 }
 
+/* See parser-defs.h.  */
+
+void
+parser_state::push_c_string (int kind, struct stoken_vector *vec)
+{
+  std::vector<std::string> data (vec->len);
+  for (int i = 0; i < vec->len; ++i)
+    data[i] = std::string (vec->tokens[i].ptr, vec->tokens[i].length);
+
+  push_new<expr::c_string_operation> ((enum c_string_type_values) kind,
+                                     std::move (data));
+}
+
+/* See parser-defs.h.  */
+
+void
+parser_state::push_symbol (const char *name, block_symbol sym)
+{
+  if (sym.symbol != nullptr)
+    {
+      if (symbol_read_needs_frame (sym.symbol))
+       block_tracker->update (sym);
+      push_new<expr::var_value_operation> (sym.symbol, sym.block);
+    }
+  else
+    {
+      struct bound_minimal_symbol msymbol = lookup_bound_minimal_symbol (name);
+      if (msymbol.minsym != NULL)
+       push_new<expr::var_msym_value_operation> (msymbol.minsym,
+                                                 msymbol.objfile);
+      else if (!have_full_symbols () && !have_partial_symbols ())
+       error (_("No symbol table is loaded.  Use the \"file\" command."));
+      else
+       error (_("No symbol \"%s\" in current context."), name);
+    }
+}
+
+/* See parser-defs.h.  */
+
+void
+parser_state::push_dollar (struct stoken str)
+{
+  struct block_symbol sym;
+  struct bound_minimal_symbol msym;
+  struct internalvar *isym = NULL;
+  std::string copy;
+
+  /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1)
+     and $$digits (equivalent to $<-digits> if you could type that).  */
+
+  int negate = 0;
+  int i = 1;
+  /* Double dollar means negate the number and add -1 as well.
+     Thus $$ alone means -1.  */
+  if (str.length >= 2 && str.ptr[1] == '$')
+    {
+      negate = 1;
+      i = 2;
+    }
+  if (i == str.length)
+    {
+      /* Just dollars (one or two).  */
+      i = -negate;
+      goto handle_last;
+    }
+  /* Is the rest of the token digits?  */
+  for (; i < str.length; i++)
+    if (!(str.ptr[i] >= '0' && str.ptr[i] <= '9'))
+      break;
+  if (i == str.length)
+    {
+      i = atoi (str.ptr + 1 + negate);
+      if (negate)
+       i = -i;
+      goto handle_last;
+    }
+
+  /* Handle tokens that refer to machine registers:
+     $ followed by a register name.  */
+  i = user_reg_map_name_to_regnum (gdbarch (),
+                                  str.ptr + 1, str.length - 1);
+  if (i >= 0)
+    goto handle_register;
+
+  /* Any names starting with $ are probably debugger internal variables.  */
+
+  copy = copy_name (str);
+  isym = lookup_only_internalvar (copy.c_str () + 1);
+  if (isym)
+    {
+      push_new<expr::internalvar_operation> (isym);
+      return;
+    }
+
+  /* On some systems, such as HP-UX and hppa-linux, certain system routines
+     have names beginning with $ or $$.  Check for those, first.  */
+
+  sym = lookup_symbol (copy.c_str (), NULL, VAR_DOMAIN, NULL);
+  if (sym.symbol)
+    {
+      push_new<expr::var_value_operation> (sym.symbol, sym.block);
+      return;
+    }
+  msym = lookup_bound_minimal_symbol (copy.c_str ());
+  if (msym.minsym)
+    {
+      push_new<expr::var_msym_value_operation> (msym.minsym, msym.objfile);
+      return;
+    }
+
+  /* Any other names are assumed to be debugger internal variables.  */
+
+  push_new<expr::internalvar_operation>
+    (create_internalvar (copy.c_str () + 1));
+  return;
+handle_last:
+  push_new<expr::last_operation> (i);
+  return;
+handle_register:
+  str.length--;
+  str.ptr++;
+  push_new<expr::register_operation> (copy_name (str));
+  block_tracker->update (expression_context_block,
+                        INNERMOST_BLOCK_FOR_REGISTERS);
+  return;
+}
+
 \f
 /* Recognize tokens that start with '$'.  These include:
 
index 9f70ff9dca7aa3a005dd9125df65e0f36b723ed8..69325596f363bef54407b9715be7ba01661c744b 100644 (file)
@@ -173,6 +173,66 @@ struct parser_state : public expr_builder
 
   void mark_completion_tag (enum type_code tag, const char *ptr, int length);
 
+  /* Push an operation on the stack.  */
+  void push (expr::operation_up &&op)
+  {
+    m_operations.push_back (std::move (op));
+  }
+
+  /* Create a new operation and push it on the stack.  */
+  template<typename T, typename... Arg>
+  void push_new (Arg... args)
+  {
+    m_operations.emplace_back (new T (std::forward<Arg> (args)...));
+  }
+
+  /* Push a new C string operation.  */
+  void push_c_string (int, struct stoken_vector *vec);
+
+  /* Push a symbol reference.  If SYM is nullptr, look for a minimal
+     symbol.  */
+  void push_symbol (const char *name, block_symbol sym);
+
+  /* Push a reference to $mumble.  This may result in a convenience
+     variable, a history reference, or a register.  */
+  void push_dollar (struct stoken str);
+
+  /* Pop an operation from the stack.  */
+  expr::operation_up pop ()
+  {
+    expr::operation_up result = std::move (m_operations.back ());
+    m_operations.pop_back ();
+    return result;
+  }
+
+  /* Pop N elements from the stack and return a vector.  */
+  std::vector<expr::operation_up> pop_vector (int n)
+  {
+    std::vector<expr::operation_up> result (n);
+    for (int i = 1; i <= n; ++i)
+      result[n - i] = pop ();
+    return result;
+  }
+
+  /* A helper that pops an operation, wraps it in some other
+     operation, and pushes it again.  */
+  template<typename T>
+  void wrap ()
+  {
+    using namespace expr;
+    operation_up v = ::expr::make_operation<T> (pop ());
+    push (std::move (v));
+  }
+
+  /* A helper that pops two operations, wraps them in some other
+     operation, and pushes the result.  */
+  template<typename T>
+  void wrap2 ()
+  {
+    expr::operation_up rhs = pop ();
+    expr::operation_up lhs = pop ();
+    push (expr::make_operation<T> (std::move (lhs), std::move (rhs)));
+  }
 
   /* If this is nonzero, this block is used as the lexical context for
      symbol names.  */
@@ -221,6 +281,9 @@ private:
      arguments contain other function calls.  */
 
   std::vector<int> m_funcall_chain;
+
+  /* Stack of operations.  */
+  std::vector<expr::operation_up> m_operations;
 };
 
 /* When parsing expressions we track the innermost block that was