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