Home | History | Annotate | Line # | Download | only in gcc
spellcheck.h revision 1.1.1.1.4.3
      1  1.1.1.1.4.2  christos /* Find near-matches for strings and identifiers.
      2  1.1.1.1.4.3    martin    Copyright (C) 2015-2017 Free Software Foundation, Inc.
      3  1.1.1.1.4.2  christos 
      4  1.1.1.1.4.2  christos This file is part of GCC.
      5  1.1.1.1.4.2  christos 
      6  1.1.1.1.4.2  christos GCC is free software; you can redistribute it and/or modify it under
      7  1.1.1.1.4.2  christos the terms of the GNU General Public License as published by the Free
      8  1.1.1.1.4.2  christos Software Foundation; either version 3, or (at your option) any later
      9  1.1.1.1.4.2  christos version.
     10  1.1.1.1.4.2  christos 
     11  1.1.1.1.4.2  christos GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     12  1.1.1.1.4.2  christos WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  1.1.1.1.4.2  christos FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  1.1.1.1.4.2  christos for more details.
     15  1.1.1.1.4.2  christos 
     16  1.1.1.1.4.2  christos You should have received a copy of the GNU General Public License
     17  1.1.1.1.4.2  christos along with GCC; see the file COPYING3.  If not see
     18  1.1.1.1.4.2  christos <http://www.gnu.org/licenses/>.  */
     19  1.1.1.1.4.2  christos 
     20  1.1.1.1.4.2  christos #ifndef GCC_SPELLCHECK_H
     21  1.1.1.1.4.2  christos #define GCC_SPELLCHECK_H
     22  1.1.1.1.4.2  christos 
     23  1.1.1.1.4.2  christos typedef unsigned int edit_distance_t;
     24  1.1.1.1.4.2  christos const edit_distance_t MAX_EDIT_DISTANCE = UINT_MAX;
     25  1.1.1.1.4.2  christos 
     26  1.1.1.1.4.2  christos /* spellcheck.c  */
     27  1.1.1.1.4.2  christos extern edit_distance_t
     28  1.1.1.1.4.2  christos levenshtein_distance (const char *s, int len_s,
     29  1.1.1.1.4.2  christos 		      const char *t, int len_t);
     30  1.1.1.1.4.2  christos 
     31  1.1.1.1.4.2  christos extern edit_distance_t
     32  1.1.1.1.4.2  christos levenshtein_distance (const char *s, const char *t);
     33  1.1.1.1.4.2  christos 
     34  1.1.1.1.4.2  christos extern const char *
     35  1.1.1.1.4.2  christos find_closest_string (const char *target,
     36  1.1.1.1.4.2  christos 		     const auto_vec<const char *> *candidates);
     37  1.1.1.1.4.2  christos 
     38  1.1.1.1.4.3    martin /* A traits class for describing a string-like type usable by
     39  1.1.1.1.4.3    martin    class best_match.
     40  1.1.1.1.4.3    martin    Specializations should provide the implementations of the following:
     41  1.1.1.1.4.3    martin 
     42  1.1.1.1.4.3    martin      static size_t get_length (TYPE);
     43  1.1.1.1.4.3    martin      static const char *get_string (TYPE);
     44  1.1.1.1.4.3    martin 
     45  1.1.1.1.4.3    martin    get_string should return a non-NULL ptr, which does not need to be
     46  1.1.1.1.4.3    martin    0-terminated.  */
     47  1.1.1.1.4.3    martin 
     48  1.1.1.1.4.3    martin template <typename TYPE>
     49  1.1.1.1.4.3    martin struct edit_distance_traits {};
     50  1.1.1.1.4.3    martin 
     51  1.1.1.1.4.3    martin /* Specialization of edit_distance_traits for C-style strings.  */
     52  1.1.1.1.4.3    martin 
     53  1.1.1.1.4.3    martin template <>
     54  1.1.1.1.4.3    martin struct edit_distance_traits<const char *>
     55  1.1.1.1.4.3    martin {
     56  1.1.1.1.4.3    martin   static size_t get_length (const char *str)
     57  1.1.1.1.4.3    martin   {
     58  1.1.1.1.4.3    martin     gcc_assert (str);
     59  1.1.1.1.4.3    martin     return strlen (str);
     60  1.1.1.1.4.3    martin   }
     61  1.1.1.1.4.3    martin 
     62  1.1.1.1.4.3    martin   static const char *get_string (const char *str)
     63  1.1.1.1.4.3    martin   {
     64  1.1.1.1.4.3    martin     gcc_assert (str);
     65  1.1.1.1.4.3    martin     return str;
     66  1.1.1.1.4.3    martin   }
     67  1.1.1.1.4.3    martin };
     68  1.1.1.1.4.3    martin 
     69  1.1.1.1.4.3    martin /* A type for use when determining the best match against a string,
     70  1.1.1.1.4.3    martin    expressed as a template so that we can match against various
     71  1.1.1.1.4.3    martin    string-like types (const char *, frontend identifiers, and preprocessor
     72  1.1.1.1.4.3    martin    macros).
     73  1.1.1.1.4.3    martin 
     74  1.1.1.1.4.3    martin    This type accumulates the best possible match against GOAL_TYPE for
     75  1.1.1.1.4.3    martin    a sequence of elements of CANDIDATE_TYPE, whilst minimizing the
     76  1.1.1.1.4.3    martin    number of calls to levenshtein_distance and to
     77  1.1.1.1.4.3    martin    edit_distance_traits<T>::get_length.  */
     78  1.1.1.1.4.3    martin 
     79  1.1.1.1.4.3    martin template <typename GOAL_TYPE, typename CANDIDATE_TYPE>
     80  1.1.1.1.4.3    martin class best_match
     81  1.1.1.1.4.3    martin {
     82  1.1.1.1.4.3    martin  public:
     83  1.1.1.1.4.3    martin   typedef GOAL_TYPE goal_t;
     84  1.1.1.1.4.3    martin   typedef CANDIDATE_TYPE candidate_t;
     85  1.1.1.1.4.3    martin   typedef edit_distance_traits<goal_t> goal_traits;
     86  1.1.1.1.4.3    martin   typedef edit_distance_traits<candidate_t> candidate_traits;
     87  1.1.1.1.4.3    martin 
     88  1.1.1.1.4.3    martin   /* Constructor.  */
     89  1.1.1.1.4.3    martin 
     90  1.1.1.1.4.3    martin   best_match (GOAL_TYPE goal,
     91  1.1.1.1.4.3    martin 	      edit_distance_t best_distance_so_far = MAX_EDIT_DISTANCE)
     92  1.1.1.1.4.3    martin   : m_goal (goal_traits::get_string (goal)),
     93  1.1.1.1.4.3    martin     m_goal_len (goal_traits::get_length (goal)),
     94  1.1.1.1.4.3    martin     m_best_candidate (NULL),
     95  1.1.1.1.4.3    martin     m_best_distance (best_distance_so_far)
     96  1.1.1.1.4.3    martin   {}
     97  1.1.1.1.4.3    martin 
     98  1.1.1.1.4.3    martin   /* Compare the edit distance between CANDIDATE and m_goal,
     99  1.1.1.1.4.3    martin      and if it's the best so far, record it.  */
    100  1.1.1.1.4.3    martin 
    101  1.1.1.1.4.3    martin   void consider (candidate_t candidate)
    102  1.1.1.1.4.3    martin   {
    103  1.1.1.1.4.3    martin     size_t candidate_len = candidate_traits::get_length (candidate);
    104  1.1.1.1.4.3    martin 
    105  1.1.1.1.4.3    martin     /* Calculate a lower bound on the candidate's distance to the goal,
    106  1.1.1.1.4.3    martin        based on the difference in lengths; it will require at least
    107  1.1.1.1.4.3    martin        this many insertions/deletions.  */
    108  1.1.1.1.4.3    martin     edit_distance_t min_candidate_distance
    109  1.1.1.1.4.3    martin       = abs ((ssize_t)candidate_len - (ssize_t)m_goal_len);
    110  1.1.1.1.4.3    martin 
    111  1.1.1.1.4.3    martin     /* If the candidate's length is sufficiently different to that
    112  1.1.1.1.4.3    martin        of the goal string, then the number of insertions/deletions
    113  1.1.1.1.4.3    martin        may be >= the best distance so far.  If so, we can reject
    114  1.1.1.1.4.3    martin        the candidate immediately without needing to compute
    115  1.1.1.1.4.3    martin        the exact distance, since it won't be an improvement.  */
    116  1.1.1.1.4.3    martin     if (min_candidate_distance >= m_best_distance)
    117  1.1.1.1.4.3    martin       return;
    118  1.1.1.1.4.3    martin 
    119  1.1.1.1.4.3    martin     /* If the candidate will be unable to beat the criterion in
    120  1.1.1.1.4.3    martin        get_best_meaningful_candidate, reject it without computing
    121  1.1.1.1.4.3    martin        the exact distance.  */
    122  1.1.1.1.4.3    martin     unsigned int cutoff = MAX (m_goal_len, candidate_len) / 2;
    123  1.1.1.1.4.3    martin     if (min_candidate_distance > cutoff)
    124  1.1.1.1.4.3    martin       return;
    125  1.1.1.1.4.3    martin 
    126  1.1.1.1.4.3    martin     /* Otherwise, compute the distance and see if the candidate
    127  1.1.1.1.4.3    martin        has beaten the previous best value.  */
    128  1.1.1.1.4.3    martin     edit_distance_t dist
    129  1.1.1.1.4.3    martin       = levenshtein_distance (m_goal, m_goal_len,
    130  1.1.1.1.4.3    martin 			      candidate_traits::get_string (candidate),
    131  1.1.1.1.4.3    martin 			      candidate_len);
    132  1.1.1.1.4.3    martin     if (dist < m_best_distance)
    133  1.1.1.1.4.3    martin       {
    134  1.1.1.1.4.3    martin 	m_best_distance = dist;
    135  1.1.1.1.4.3    martin 	m_best_candidate = candidate;
    136  1.1.1.1.4.3    martin 	m_best_candidate_len = candidate_len;
    137  1.1.1.1.4.3    martin       }
    138  1.1.1.1.4.3    martin   }
    139  1.1.1.1.4.3    martin 
    140  1.1.1.1.4.3    martin   /* Assuming that BEST_CANDIDATE is known to be better than
    141  1.1.1.1.4.3    martin      m_best_candidate, update (without recomputing the edit distance to
    142  1.1.1.1.4.3    martin      the goal).  */
    143  1.1.1.1.4.3    martin 
    144  1.1.1.1.4.3    martin   void set_best_so_far (CANDIDATE_TYPE best_candidate,
    145  1.1.1.1.4.3    martin 			edit_distance_t best_distance,
    146  1.1.1.1.4.3    martin 			size_t best_candidate_len)
    147  1.1.1.1.4.3    martin   {
    148  1.1.1.1.4.3    martin     gcc_assert (best_distance < m_best_distance);
    149  1.1.1.1.4.3    martin     m_best_candidate = best_candidate;
    150  1.1.1.1.4.3    martin     m_best_distance = best_distance;
    151  1.1.1.1.4.3    martin     m_best_candidate_len = best_candidate_len;
    152  1.1.1.1.4.3    martin   }
    153  1.1.1.1.4.3    martin 
    154  1.1.1.1.4.3    martin   /* Get the best candidate so far, but applying a filter to ensure
    155  1.1.1.1.4.3    martin      that we return NULL if none of the candidates are close to the goal,
    156  1.1.1.1.4.3    martin      to avoid offering nonsensical suggestions to the user.  */
    157  1.1.1.1.4.3    martin 
    158  1.1.1.1.4.3    martin   candidate_t get_best_meaningful_candidate () const
    159  1.1.1.1.4.3    martin   {
    160  1.1.1.1.4.3    martin     /* If more than half of the letters were misspelled, the suggestion is
    161  1.1.1.1.4.3    martin        likely to be meaningless.  */
    162  1.1.1.1.4.3    martin     if (m_best_candidate)
    163  1.1.1.1.4.3    martin       {
    164  1.1.1.1.4.3    martin 	unsigned int cutoff = MAX (m_goal_len, m_best_candidate_len) / 2;
    165  1.1.1.1.4.3    martin 	if (m_best_distance > cutoff)
    166  1.1.1.1.4.3    martin 	  return NULL;
    167  1.1.1.1.4.3    martin     }
    168  1.1.1.1.4.3    martin 
    169  1.1.1.1.4.3    martin     /* If the goal string somehow makes it into the candidate list, offering
    170  1.1.1.1.4.3    martin        it as a suggestion will be nonsensical e.g.
    171  1.1.1.1.4.3    martin          'constexpr' does not name a type; did you mean 'constexpr'?
    172  1.1.1.1.4.3    martin        Ultimately such suggestions are due to bugs in constructing the
    173  1.1.1.1.4.3    martin        candidate list, but as a band-aid, do not offer suggestions for
    174  1.1.1.1.4.3    martin        distance == 0 (where candidate == goal).  */
    175  1.1.1.1.4.3    martin     if (m_best_distance == 0)
    176  1.1.1.1.4.3    martin       return NULL;
    177  1.1.1.1.4.3    martin 
    178  1.1.1.1.4.3    martin     return m_best_candidate;
    179  1.1.1.1.4.3    martin   }
    180  1.1.1.1.4.3    martin 
    181  1.1.1.1.4.3    martin   edit_distance_t get_best_distance () const { return m_best_distance; }
    182  1.1.1.1.4.3    martin   size_t get_best_candidate_length () const { return m_best_candidate_len; }
    183  1.1.1.1.4.3    martin 
    184  1.1.1.1.4.3    martin  private:
    185  1.1.1.1.4.3    martin   const char *m_goal;
    186  1.1.1.1.4.3    martin   size_t m_goal_len;
    187  1.1.1.1.4.3    martin   candidate_t m_best_candidate;
    188  1.1.1.1.4.3    martin   edit_distance_t m_best_distance;
    189  1.1.1.1.4.3    martin   size_t m_best_candidate_len;
    190  1.1.1.1.4.3    martin };
    191  1.1.1.1.4.2  christos 
    192  1.1.1.1.4.2  christos #endif  /* GCC_SPELLCHECK_H  */
    193