| /* Parse expressions for GDB. | 
 |  | 
 |    Copyright (C) 1986-2022 Free Software Foundation, Inc. | 
 |  | 
 |    Modified from expread.y by the Department of Computer Science at the | 
 |    State University of New York at Buffalo, 1991. | 
 |  | 
 |    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/>.  */ | 
 |  | 
 | /* Parse an expression from text in a string, | 
 |    and return the result as a struct expression pointer. | 
 |    That structure contains arithmetic operations in reverse polish, | 
 |    with constants represented by operations that are followed by special data. | 
 |    See expression.h for the details of the format. | 
 |    What is important here is that it can be built up sequentially | 
 |    during the process of parsing; the lower levels of the tree always | 
 |    come first in the result.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include <ctype.h> | 
 | #include "arch-utils.h" | 
 | #include "symtab.h" | 
 | #include "gdbtypes.h" | 
 | #include "frame.h" | 
 | #include "expression.h" | 
 | #include "value.h" | 
 | #include "command.h" | 
 | #include "language.h" | 
 | #include "parser-defs.h" | 
 | #include "gdbcmd.h" | 
 | #include "symfile.h"		/* for overlay functions */ | 
 | #include "inferior.h" | 
 | #include "target-float.h" | 
 | #include "block.h" | 
 | #include "source.h" | 
 | #include "objfiles.h" | 
 | #include "user-regs.h" | 
 | #include <algorithm> | 
 | #include "gdbsupport/gdb_optional.h" | 
 | #include "c-exp.h" | 
 |  | 
 | static unsigned int expressiondebug = 0; | 
 | static void | 
 | show_expressiondebug (struct ui_file *file, int from_tty, | 
 | 		      struct cmd_list_element *c, const char *value) | 
 | { | 
 |   gdb_printf (file, _("Expression debugging is %s.\n"), value); | 
 | } | 
 |  | 
 |  | 
 | /* True if an expression parser should set yydebug.  */ | 
 | bool parser_debug; | 
 |  | 
 | static void | 
 | show_parserdebug (struct ui_file *file, int from_tty, | 
 | 		  struct cmd_list_element *c, const char *value) | 
 | { | 
 |   gdb_printf (file, _("Parser debugging is %s.\n"), value); | 
 | } | 
 |  | 
 |  | 
 | static expression_up parse_exp_in_context | 
 |      (const char **, CORE_ADDR, | 
 |       const struct block *, int, | 
 |       bool, innermost_block_tracker *, | 
 |       std::unique_ptr<expr_completion_base> *); | 
 |  | 
 | /* Documented at it's declaration.  */ | 
 |  | 
 | void | 
 | innermost_block_tracker::update (const struct block *b, | 
 | 				 innermost_block_tracker_types t) | 
 | { | 
 |   if ((m_types & t) != 0 | 
 |       && (m_innermost_block == NULL | 
 | 	  || contained_in (b, m_innermost_block))) | 
 |     m_innermost_block = b; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /* Return the type of MSYMBOL, a minimal symbol of OBJFILE.  If | 
 |    ADDRESS_P is not NULL, set it to the MSYMBOL's resolved | 
 |    address.  */ | 
 |  | 
 | type * | 
 | find_minsym_type_and_address (minimal_symbol *msymbol, | 
 | 			      struct objfile *objfile, | 
 | 			      CORE_ADDR *address_p) | 
 | { | 
 |   bound_minimal_symbol bound_msym = {msymbol, objfile}; | 
 |   struct obj_section *section = msymbol->obj_section (objfile); | 
 |   enum minimal_symbol_type type = MSYMBOL_TYPE (msymbol); | 
 |  | 
 |   bool is_tls = (section != NULL | 
 | 		 && section->the_bfd_section->flags & SEC_THREAD_LOCAL); | 
 |  | 
 |   /* The minimal symbol might point to a function descriptor; | 
 |      resolve it to the actual code address instead.  */ | 
 |   CORE_ADDR addr; | 
 |   if (is_tls) | 
 |     { | 
 |       /* Addresses of TLS symbols are really offsets into a | 
 | 	 per-objfile/per-thread storage block.  */ | 
 |       addr = MSYMBOL_VALUE_RAW_ADDRESS (bound_msym.minsym); | 
 |     } | 
 |   else if (msymbol_is_function (objfile, msymbol, &addr)) | 
 |     { | 
 |       if (addr != BMSYMBOL_VALUE_ADDRESS (bound_msym)) | 
 | 	{ | 
 | 	  /* This means we resolved a function descriptor, and we now | 
 | 	     have an address for a code/text symbol instead of a data | 
 | 	     symbol.  */ | 
 | 	  if (MSYMBOL_TYPE (msymbol) == mst_data_gnu_ifunc) | 
 | 	    type = mst_text_gnu_ifunc; | 
 | 	  else | 
 | 	    type = mst_text; | 
 | 	  section = NULL; | 
 | 	} | 
 |     } | 
 |   else | 
 |     addr = BMSYMBOL_VALUE_ADDRESS (bound_msym); | 
 |  | 
 |   if (overlay_debugging) | 
 |     addr = symbol_overlayed_address (addr, section); | 
 |  | 
 |   if (is_tls) | 
 |     { | 
 |       /* Skip translation if caller does not need the address.  */ | 
 |       if (address_p != NULL) | 
 | 	*address_p = target_translate_tls_address (objfile, addr); | 
 |       return objfile_type (objfile)->nodebug_tls_symbol; | 
 |     } | 
 |  | 
 |   if (address_p != NULL) | 
 |     *address_p = addr; | 
 |  | 
 |   switch (type) | 
 |     { | 
 |     case mst_text: | 
 |     case mst_file_text: | 
 |     case mst_solib_trampoline: | 
 |       return objfile_type (objfile)->nodebug_text_symbol; | 
 |  | 
 |     case mst_text_gnu_ifunc: | 
 |       return objfile_type (objfile)->nodebug_text_gnu_ifunc_symbol; | 
 |  | 
 |     case mst_data: | 
 |     case mst_file_data: | 
 |     case mst_bss: | 
 |     case mst_file_bss: | 
 |       return objfile_type (objfile)->nodebug_data_symbol; | 
 |  | 
 |     case mst_slot_got_plt: | 
 |       return objfile_type (objfile)->nodebug_got_plt_symbol; | 
 |  | 
 |     default: | 
 |       return objfile_type (objfile)->nodebug_unknown_symbol; | 
 |     } | 
 | } | 
 |  | 
 | bool | 
 | expr_complete_tag::complete (struct expression *exp, | 
 | 			     completion_tracker &tracker) | 
 | { | 
 |   collect_symbol_completion_matches_type (tracker, m_name.get (), | 
 | 					  m_name.get (), m_code); | 
 |   return true; | 
 | } | 
 |  | 
 | /* See parser-defs.h.  */ | 
 |  | 
 | void | 
 | parser_state::mark_struct_expression (expr::structop_base_operation *op) | 
 | { | 
 |   gdb_assert (parse_completion && m_completion_state == nullptr); | 
 |   m_completion_state.reset (new expr_complete_structop (op)); | 
 | } | 
 |  | 
 | /* Indicate that the current parser invocation is completing a tag. | 
 |    TAG is the type code of the tag, and PTR and LENGTH represent the | 
 |    start of the tag name.  */ | 
 |  | 
 | void | 
 | parser_state::mark_completion_tag (enum type_code tag, const char *ptr, | 
 | 				   int length) | 
 | { | 
 |   gdb_assert (parse_completion && m_completion_state == nullptr); | 
 |   gdb_assert (tag == TYPE_CODE_UNION | 
 | 	      || tag == TYPE_CODE_STRUCT | 
 | 	      || tag == TYPE_CODE_ENUM); | 
 |   m_completion_state.reset | 
 |     (new expr_complete_tag (tag, make_unique_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); | 
 |     } | 
 |   else | 
 |     { | 
 |       struct bound_minimal_symbol msymbol = lookup_bound_minimal_symbol (name); | 
 |       if (msymbol.minsym != NULL) | 
 | 	push_new<expr::var_msym_value_operation> (msymbol); | 
 |       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); | 
 |       return; | 
 |     } | 
 |   msym = lookup_bound_minimal_symbol (copy.c_str ()); | 
 |   if (msym.minsym) | 
 |     { | 
 |       push_new<expr::var_msym_value_operation> (msym); | 
 |       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; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | const char * | 
 | find_template_name_end (const char *p) | 
 | { | 
 |   int depth = 1; | 
 |   int just_seen_right = 0; | 
 |   int just_seen_colon = 0; | 
 |   int just_seen_space = 0; | 
 |  | 
 |   if (!p || (*p != '<')) | 
 |     return 0; | 
 |  | 
 |   while (*++p) | 
 |     { | 
 |       switch (*p) | 
 | 	{ | 
 | 	case '\'': | 
 | 	case '\"': | 
 | 	case '{': | 
 | 	case '}': | 
 | 	  /* In future, may want to allow these??  */ | 
 | 	  return 0; | 
 | 	case '<': | 
 | 	  depth++;		/* start nested template */ | 
 | 	  if (just_seen_colon || just_seen_right || just_seen_space) | 
 | 	    return 0;		/* but not after : or :: or > or space */ | 
 | 	  break; | 
 | 	case '>': | 
 | 	  if (just_seen_colon || just_seen_right) | 
 | 	    return 0;		/* end a (nested?) template */ | 
 | 	  just_seen_right = 1;	/* but not after : or :: */ | 
 | 	  if (--depth == 0)	/* also disallow >>, insist on > > */ | 
 | 	    return ++p;		/* if outermost ended, return */ | 
 | 	  break; | 
 | 	case ':': | 
 | 	  if (just_seen_space || (just_seen_colon > 1)) | 
 | 	    return 0;		/* nested class spec coming up */ | 
 | 	  just_seen_colon++;	/* we allow :: but not :::: */ | 
 | 	  break; | 
 | 	case ' ': | 
 | 	  break; | 
 | 	default: | 
 | 	  if (!((*p >= 'a' && *p <= 'z') ||	/* allow token chars */ | 
 | 		(*p >= 'A' && *p <= 'Z') || | 
 | 		(*p >= '0' && *p <= '9') || | 
 | 		(*p == '_') || (*p == ',') ||	/* commas for template args */ | 
 | 		(*p == '&') || (*p == '*') ||	/* pointer and ref types */ | 
 | 		(*p == '(') || (*p == ')') ||	/* function types */ | 
 | 		(*p == '[') || (*p == ']')))	/* array types */ | 
 | 	    return 0; | 
 | 	} | 
 |       if (*p != ' ') | 
 | 	just_seen_space = 0; | 
 |       if (*p != ':') | 
 | 	just_seen_colon = 0; | 
 |       if (*p != '>') | 
 | 	just_seen_right = 0; | 
 |     } | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 | /* Return a null-terminated temporary copy of the name of a string token. | 
 |  | 
 |    Tokens that refer to names do so with explicit pointer and length, | 
 |    so they can share the storage that lexptr is parsing. | 
 |    When it is necessary to pass a name to a function that expects | 
 |    a null-terminated string, the substring is copied out | 
 |    into a separate block of storage.  */ | 
 |  | 
 | std::string | 
 | copy_name (struct stoken token) | 
 | { | 
 |   return std::string (token.ptr, token.length); | 
 | } | 
 |  | 
 |  | 
 | /* Read an expression from the string *STRINGPTR points to, | 
 |    parse it, and return a pointer to a struct expression that we malloc. | 
 |    Use block BLOCK as the lexical context for variable names; | 
 |    if BLOCK is zero, use the block of the selected stack frame. | 
 |    Meanwhile, advance *STRINGPTR to point after the expression, | 
 |    at the first nonwhite character that is not part of the expression | 
 |    (possibly a null character). | 
 |  | 
 |    If COMMA is nonzero, stop if a comma is reached.  */ | 
 |  | 
 | expression_up | 
 | parse_exp_1 (const char **stringptr, CORE_ADDR pc, const struct block *block, | 
 | 	     int comma, innermost_block_tracker *tracker) | 
 | { | 
 |   return parse_exp_in_context (stringptr, pc, block, comma, false, | 
 | 			       tracker, nullptr); | 
 | } | 
 |  | 
 | /* As for parse_exp_1, except that if VOID_CONTEXT_P, then | 
 |    no value is expected from the expression.  */ | 
 |  | 
 | static expression_up | 
 | parse_exp_in_context (const char **stringptr, CORE_ADDR pc, | 
 | 		      const struct block *block, | 
 | 		      int comma, bool void_context_p, | 
 | 		      innermost_block_tracker *tracker, | 
 | 		      std::unique_ptr<expr_completion_base> *completer) | 
 | { | 
 |   const struct language_defn *lang = NULL; | 
 |  | 
 |   if (*stringptr == 0 || **stringptr == 0) | 
 |     error_no_arg (_("expression to compute")); | 
 |  | 
 |   const struct block *expression_context_block = block; | 
 |   CORE_ADDR expression_context_pc = 0; | 
 |  | 
 |   innermost_block_tracker local_tracker; | 
 |   if (tracker == nullptr) | 
 |     tracker = &local_tracker; | 
 |  | 
 |   /* If no context specified, try using the current frame, if any.  */ | 
 |   if (!expression_context_block) | 
 |     expression_context_block = get_selected_block (&expression_context_pc); | 
 |   else if (pc == 0) | 
 |     expression_context_pc = BLOCK_ENTRY_PC (expression_context_block); | 
 |   else | 
 |     expression_context_pc = pc; | 
 |  | 
 |   /* Fall back to using the current source static context, if any.  */ | 
 |  | 
 |   if (!expression_context_block) | 
 |     { | 
 |       struct symtab_and_line cursal = get_current_source_symtab_and_line (); | 
 |       if (cursal.symtab) | 
 | 	expression_context_block | 
 | 	  = BLOCKVECTOR_BLOCK (cursal.symtab->blockvector (), | 
 | 			       STATIC_BLOCK); | 
 |       if (expression_context_block) | 
 | 	expression_context_pc = BLOCK_ENTRY_PC (expression_context_block); | 
 |     } | 
 |  | 
 |   if (language_mode == language_mode_auto && block != NULL) | 
 |     { | 
 |       /* Find the language associated to the given context block. | 
 | 	 Default to the current language if it can not be determined. | 
 |  | 
 | 	 Note that using the language corresponding to the current frame | 
 | 	 can sometimes give unexpected results.  For instance, this | 
 | 	 routine is often called several times during the inferior | 
 | 	 startup phase to re-parse breakpoint expressions after | 
 | 	 a new shared library has been loaded.  The language associated | 
 | 	 to the current frame at this moment is not relevant for | 
 | 	 the breakpoint.  Using it would therefore be silly, so it seems | 
 | 	 better to rely on the current language rather than relying on | 
 | 	 the current frame language to parse the expression.  That's why | 
 | 	 we do the following language detection only if the context block | 
 | 	 has been specifically provided.  */ | 
 |       struct symbol *func = block_linkage_function (block); | 
 |  | 
 |       if (func != NULL) | 
 | 	lang = language_def (func->language ()); | 
 |       if (lang == NULL || lang->la_language == language_unknown) | 
 | 	lang = current_language; | 
 |     } | 
 |   else | 
 |     lang = current_language; | 
 |  | 
 |   /* get_current_arch may reset CURRENT_LANGUAGE via select_frame. | 
 |      While we need CURRENT_LANGUAGE to be set to LANG (for lookup_symbol | 
 |      and others called from *.y) ensure CURRENT_LANGUAGE gets restored | 
 |      to the value matching SELECTED_FRAME as set by get_current_arch.  */ | 
 |  | 
 |   parser_state ps (lang, get_current_arch (), expression_context_block, | 
 | 		   expression_context_pc, comma, *stringptr, | 
 | 		   completer != nullptr, tracker, void_context_p); | 
 |  | 
 |   scoped_restore_current_language lang_saver; | 
 |   set_language (lang->la_language); | 
 |  | 
 |   try | 
 |     { | 
 |       lang->parser (&ps); | 
 |     } | 
 |   catch (const gdb_exception &except) | 
 |     { | 
 |       /* If parsing for completion, allow this to succeed; but if no | 
 | 	 expression elements have been written, then there's nothing | 
 | 	 to do, so fail.  */ | 
 |       if (! ps.parse_completion || ps.expout->op == nullptr) | 
 | 	throw; | 
 |     } | 
 |  | 
 |   expression_up result = ps.release (); | 
 |   result->op->set_outermost (); | 
 |  | 
 |   if (expressiondebug) | 
 |     dump_prefix_expression (result.get (), gdb_stdlog); | 
 |  | 
 |   if (completer != nullptr) | 
 |     *completer = std::move (ps.m_completion_state); | 
 |   *stringptr = ps.lexptr; | 
 |   return result; | 
 | } | 
 |  | 
 | /* Parse STRING as an expression, and complain if this fails to use up | 
 |    all of the contents of STRING.  TRACKER, if non-null, will be | 
 |    updated by the parser.  VOID_CONTEXT_P should be true to indicate | 
 |    that the expression may be expected to return a value with void | 
 |    type.  Parsers are free to ignore this, or to use it to help with | 
 |    overload resolution decisions.  */ | 
 |  | 
 | expression_up | 
 | parse_expression (const char *string, innermost_block_tracker *tracker, | 
 | 		  bool void_context_p) | 
 | { | 
 |   expression_up exp = parse_exp_in_context (&string, 0, nullptr, 0, | 
 | 					    void_context_p, | 
 | 					    tracker, nullptr); | 
 |   if (*string) | 
 |     error (_("Junk after end of expression.")); | 
 |   return exp; | 
 | } | 
 |  | 
 | /* Same as parse_expression, but using the given language (LANG) | 
 |    to parse the expression.  */ | 
 |  | 
 | expression_up | 
 | parse_expression_with_language (const char *string, enum language lang) | 
 | { | 
 |   gdb::optional<scoped_restore_current_language> lang_saver; | 
 |   if (current_language->la_language != lang) | 
 |     { | 
 |       lang_saver.emplace (); | 
 |       set_language (lang); | 
 |     } | 
 |  | 
 |   return parse_expression (string); | 
 | } | 
 |  | 
 | /* Parse STRING as an expression.  If the parse is marked for | 
 |    completion, set COMPLETER and return the expression.  In all other | 
 |    cases, return NULL.  */ | 
 |  | 
 | expression_up | 
 | parse_expression_for_completion | 
 |      (const char *string, | 
 |       std::unique_ptr<expr_completion_base> *completer) | 
 | { | 
 |   expression_up exp; | 
 |  | 
 |   try | 
 |     { | 
 |       exp = parse_exp_in_context (&string, 0, 0, 0, false, nullptr, completer); | 
 |     } | 
 |   catch (const gdb_exception_error &except) | 
 |     { | 
 |       /* Nothing, EXP remains NULL.  */ | 
 |     } | 
 |  | 
 |   /* If we didn't get a completion result, be sure to also not return | 
 |      an expression to our caller.  */ | 
 |   if (*completer == nullptr) | 
 |     return nullptr; | 
 |  | 
 |   return exp; | 
 | } | 
 |  | 
 | /* Parse floating point value P of length LEN. | 
 |    Return false if invalid, true if valid. | 
 |    The successfully parsed number is stored in DATA in | 
 |    target format for floating-point type TYPE. | 
 |  | 
 |    NOTE: This accepts the floating point syntax that sscanf accepts.  */ | 
 |  | 
 | bool | 
 | parse_float (const char *p, int len, | 
 | 	     const struct type *type, gdb_byte *data) | 
 | { | 
 |   return target_float_from_string (data, type, std::string (p, len)); | 
 | } | 
 |  | 
 | /* This function avoids direct calls to fprintf  | 
 |    in the parser generated debug code.  */ | 
 | void | 
 | parser_fprintf (FILE *x, const char *y, ...) | 
 | {  | 
 |   va_list args; | 
 |  | 
 |   va_start (args, y); | 
 |   if (x == stderr) | 
 |     gdb_vprintf (gdb_stderr, y, args);  | 
 |   else | 
 |     { | 
 |       gdb_printf (gdb_stderr, " Unknown FILE used.\n"); | 
 |       gdb_vprintf (gdb_stderr, y, args); | 
 |     } | 
 |   va_end (args); | 
 | } | 
 |  | 
 | /* Return rue if EXP uses OBJFILE (and will become dangling when | 
 |    OBJFILE is unloaded), otherwise return false.  OBJFILE must not be | 
 |    a separate debug info file.  */ | 
 |  | 
 | bool | 
 | exp_uses_objfile (struct expression *exp, struct objfile *objfile) | 
 | { | 
 |   gdb_assert (objfile->separate_debug_objfile_backlink == NULL); | 
 |  | 
 |   return exp->op->uses_objfile (objfile); | 
 | } | 
 |  | 
 | void _initialize_parse (); | 
 | void | 
 | _initialize_parse () | 
 | { | 
 |   add_setshow_zuinteger_cmd ("expression", class_maintenance, | 
 | 			     &expressiondebug, | 
 | 			     _("Set expression debugging."), | 
 | 			     _("Show expression debugging."), | 
 | 			     _("When non-zero, the internal representation " | 
 | 			       "of expressions will be printed."), | 
 | 			     NULL, | 
 | 			     show_expressiondebug, | 
 | 			     &setdebuglist, &showdebuglist); | 
 |   add_setshow_boolean_cmd ("parser", class_maintenance, | 
 | 			    &parser_debug, | 
 | 			   _("Set parser debugging."), | 
 | 			   _("Show parser debugging."), | 
 | 			   _("When non-zero, expression parser " | 
 | 			     "tracing will be enabled."), | 
 | 			    NULL, | 
 | 			    show_parserdebug, | 
 | 			    &setdebuglist, &showdebuglist); | 
 | } |